【Top】基础算法模板
摘要
本文所涉及到的算法和数据结果模板均来自于Acwing的算法基础课和算法提高课(部分)。本文仅给出算法代码与时空复杂度,具体证明和讲解暂无。
大数运算
#include
#include
#include
using namespace std;
//由低位到高位储存 114514 -> {4 1 5 4 1 1}
typedef vector bign;
//大正整数计算
//判断a>=b
bool cmp(bign& a, bign& b){
if(a.size()==b.size()){
for(int i=a.size()-1;i>=0;i--)
if(a[i]!=b[i])return a[i]>b[i];
return true;
}
return a.size()>b.size();
}
bign add(bign& a, bign& b){
bign r;
int t = 0;
for(int i=0;i=B
bign sub(bign& a, bign& b){
bign r;
for(int i=0,t=0;i1&&r.back()==0)r.pop_back();
return r;
}
//大整数与小整数乘法
bign mul(bign& a, int b){
bign r;
int t=0;
for(int i=0;i1&&r.back()==0)r.pop_back();
return r;
}
#include
//大整数与小整数的除法
// c是余数,通过引用回传
bign div(bign& a, int b, int& c){
bign r;
c=0;
for(int i=a.size()-1;i>=0;i--){
c = c*10+a[i];
r.push_back(c/b);
c%=b;
}
reverse(r.begin(),r.end());
while(r.size()>1&&r.back()==0)r.pop_back();
return r;
}
基础排序
归并排序
- 时间复杂度 \(O(n\log n)\)
//归并排序 稳定算法
#include
using namespace std;
const int len = 1000;
int help[len];
void mergeSort(int nums[], int l, int r){
if(l>=r)return ;
int mid = l+r>>1;
mergeSort(nums,l,mid);
mergeSort(nums,mid+1,r);
int top = 0,i=l,j=mid+1;
while(i<=mid&&j<=r)
if(nums[i]
快速排序
- 时间复杂度 最佳 \(O(n\log n)\) 最坏 \(O(n^2)\)
//快速排序 非稳定算法
#include
#include
using namespace std;
void qsort(int nums[], int l, int r){
if(l>=r)return;
//枢纽量 左指针 右指针
int x = nums[l], i = l-1,j=r+1;
while(ix);
if(i
前缀和与差分
前缀和
//一维前缀和
#include
using namespace std;
const int N = 100010;
int num[N], pre[N];
//询问区间和
inline int q(int i,int j){
return pre[j]-pre[i-1];
}
int main(){
ios::sync_with_stdio(false);//提高cin的速率,无法使用scanf
// 数据规模>=10^6 推荐使用scanf
int n;
cin>>n;
//前缀和的原数组建议从1开始编号
for(int i=1;i>num[i];
for(int i=1;i
//二维前缀和
#include
using namespace std;
const int N = 1010;
//Sij代表x[1,i]y[1,j]矩阵内所有元素之和
int s[N][N];
int num[N][N];
//查询x[x1,x2]y[y1,y2]矩阵内和
inline int q(int x1,int y1,int x2,int y2){
return s[x2][y2]+s[x1-1][y1-1]-s[x2][y1-1]-s[x1-1][y2];
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>num[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+num[i][j];
return 0;
}
差分
//一维差分
//对于序列{a1,a2,...,an}
//构造序列{b1,b2,...,bn},使得ai = b1+b2+...+bi
//b1 = a1
//b2 = a2 - a1
//bi = ai - a(i-1)
//{b}称为{a}的差分
//用处:
// [l,r]+c O(n)
// ---> O(1)
//差分数组只有插入操作[l,r,c]
#include
using namespace std;
const int N = 100010;
int s[N];
//差分数组
int p[N];
void insert(int l, int r, int c){
p[l] += c;
p[r] -= c;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>s[i];
//差分数组初始化
for(int i=1;i<=n;i++)insert(i,i,s[i]);
return 0;
}
//二维差分
#include
using namespace std;
const int N = 1010;
//p是差分数组
int p[N][N],s[N][N];
void insert(int x1, int y1, int x2, int y2, int c){
p[x1][y1]+=c,p[x2+1][y1]-=c,p[x1][y2+1]-=c,p[x2+1][y2+1]+=c;
}
int main(){
int n,m,k;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>s[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)insert(i,j,i,j,s[i][j]);
return 0;
}
双指针
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
二分
- 时间复杂度 \(O(\log n)\)
整数二分
判左二分 [a,b] -> [a,mid] [mid+1 , b]
int l = 0, r = len;
while(l
判右二分 [a,b] -> [a,mid-1] [mid , b]
int l = 0, r = len;
while(l
浮点数二分
double l = 0, r = len;
while(fabs(l-r)>eps/*精度 取题目要求的精度小两个数量级*/){
double mid = (l+r)/2;
if(/**/)l = mid;
else r = mid;
}
return l;
其他基础算法
离散化
vector alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; // 映射到1, 2, ...n
}
区间合并
// 将所有存在交集的区间合并
void merge(vector &segs)
{
vector res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs)
if (ed < seg.first)
{
if (st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else ed = max(ed, seg.second);
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
图论基础算法
单源最短路
Dijistra算法
- 时间复杂度 \(O(nm)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
using namespace std;
//最大节点数
const int N = 510;
//邻接矩阵 d[a][b] a->b的权重
//最短距离 d[i] 1->path->i的权值和 d[1]=0
//st[i] 点i是否已经求得最短距离
int d[N][N],dist[N],st[N];
int main(){
//全部初始化为极大值
memset(d,0x3f,sizeof d);
memset(dist,0x3f,sizeof dist);
//点数n,边数m
int n,m;
cin>>n>>m;
while(m--){
// a->b 权重c
int a,b,c;
cin>>a>>b>>c;
//可能存在重边,取最小权重即可
d[a][b] = min(d[a][b],c);
}
//DJ
dist[1]=0;
for(int i=0;ipath->t距离最小的t
int t = -1;
for(int j=1;j<=n;j++){
if(!st[j] && (t == -1 || dist[j]path->n仍然为初始化的极大值,则说明1到n无法到达
cout<< (dist[n]==0x3f3f3f3f?-1:dist[n])<
堆优化的Dijistra算法
- 时间复杂度 \(O(m\log n)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
#include
#include
using namespace std;
const int N = 150010;
typedef pair PII;//first 路径长 second 终点节点的编号
//使用邻接表储存稀疏图
int h[N],e[N],en[N],eg[N],idx;
int n,m;
int dist[N],st[N];
void add(int a, int b, int v){
e[idx] = b;en[idx] = h[a];h[a] = idx;eg[idx++] = v;
}
int main(){
//最小堆
priority_queue,greater>heap;
memset(h,-1,sizeof h);
memset(dist,0x3f,sizeof dist);
//存图
cin>>n>>m;
for(int i=0;i>a>>b>>c;
add(a,b,c);
}
//堆优化的DJ算法
dist[1] = 0;
heap.push({0,1});
while(heap.size()){
//弹取最小路径
auto m = heap.top();
heap.pop();
//i 终点节点编号 j 路径长
int i = m.second, j = m.first;
//如果i已经确定最短路径,则跳过
if(st[i])continue;
else st[i]=true;
for(int t = h[i];t!=-1;t = en[t]){
//e[t] 下一节点编号
if(dist[e[t]] > j + eg[t]){
dist[e[t]] = j+eg[t];
heap.push({dist[e[t]],e[t]});
}
}
}
cout<<(dist[n]==0x3f3f3f3f?-1:dist[n])<
Bellman-Ford算法
- 时间复杂度 \(O(nm)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
const int N = 510,M = 100010,INF = 0x3f3f3f3f;
struct edge{
int a,b,w;
}edges[M];
int dist[N],backup[N];
int n,m;
//计算含边数不超过k条的最短路
void bf(int k){
memset(dist,0x3f,sizeof dist);
for(int i=0;i>n>>m;
for(int i=0;i>a>>b>>v;
edges[i] = {a,b,v};
}
bf(n-1);
if(dist[n]>INF/2)cout<<"No"<
Bellman-Ford判负权回路
- 时间复杂度 \(O(nm)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
using namespace std;
const int N = 2020, M = 10010, MAX = 0x3f3f3f3f;
struct ed{int a,b,w;} eds[M];
int dist[N], bp[N];
int main(){
int n,m;
scanf("%d%d", &n, &m);
for(int i = 0;idist[a]+w){neg = true;break;}
}
if(neg)cout<<"Yes"<
SPFA算法
- 时间复杂度 : 最佳 \(O(m)\) 最坏 \(O(nm)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
using namespace std;
const int N = 100010;
int n,m;
//邻接表储存稠密图
int h[N],e[N],ne[N],w[N],idx=0;
void add(int a, int b, int v){
e[idx] = b,ne[idx] =h[a],w[idx] = v, h[a] = idx++;
}
int dist[N];
//队列
int q[N],ff=0,tt=-1;
bool st[N];
int main(){
memset(h,-1,sizeof h);
memset(dist,0x3f,sizeof dist);
//读图
scanf("%d%d",&n,&m);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
//将起点纳入队列
st[1] = true;
q[++tt] = 1;
dist[1] = 0;
//BFS
while(ff<=tt){
int t = q[ff++];
st[t] = false;
//搜索所有旁支
for(int i = h[t];i!=-1;i = ne[i]){
int j = e[i];
//可更新,则更新,如果也没在队列,则加入队列
if(dist[j]>dist[t]+w[i]){
dist[j] = dist[t]+w[i];
if(!st[j]){
q[++tt] = j;
st[j] = true;
}
}
}
}
//dist[i]为初始极大值时,说明不连通
if(dist[n]==0x3f3f3f3f)puts("impossible");
else cout<
SPFA判负权回路
- 时间复杂度 : 最佳 \(O(m)\) 最坏 \(O(nm)\) $m:边的数量 $ \(n : 点的数量\)
#include
#include
#include
using namespace std;
const int N = 2020, M = 10010;
int n,m;
int h[N],e[M],ne[M],w[M],idx=0;
void add(int a, int b, int v){
e[idx] = b,ne[idx] = h[a],w[idx] = v,h[a] = idx++;
}
int dist[N],st[N];
//储存1到i的路径边数
int cnt[N];
int main(){
memset(h,-1,sizeof h);
//注意 dist初始化为0
//memset(dist,0x3f,sizeof dist);
cin>>n>>m;
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
queue q;
//由于有可能不会遍历完所有点,所以将所有点都纳入队列;
for(int i=1;i<=n;i++){
st[i]=true;
q.push(i);
}
while(q.size()){
int t = q.front();
q.pop();
st[t] = false;
for(int i=h[t];i!=-1;i = ne[i]){
int j = e[i];
if(dist[j]>dist[t]+w[i]){
dist[j] = dist[t]+w[i];
//维护路径边数
cnt[j] = cnt[t]+1;
//含有n的节点的图的最短路路径最大为n-1
//存在负权回路是会超过这个值
if(cnt[j]>=n){
cout<<"Yes"<
多源最短路
Floyd算法
- 时间复杂度 \(O(n^3)\) \(n : 点的数量\)
#include
using namespace std;
const int N = 210,M = 20020,INF = 1e9;
//d [i][j] i到j的最短路径
int d[N][N];
int n,m,k;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
if(i == j)d[i][i] = 0;
else d[i][j]=INF;
}
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
d[a][b] = min(d[a][b],c);
}
//Floyd
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
}
}
}
//end
while(k--){
int a,b;
scanf("%d%d",&a,&b);
if(d[a][b]
最小生成树
Prim算法
#include
#include
using namespace std;
const int N = 550, INF = 0x3f3f3f3f;
int n,m;
int d[N][N],dist[N];
bool st[N];
int prim(){
int ans = 0;
for(int i = 0;idist[j]))t = j;
}
st[t] = true;
if(i && dist[t]==INF)return INF;
if(i) ans += dist[t];
for(int j = 1;j<=n;j++)dist[j] = min(dist[j],d[t][j]);
}
return ans;
}
int main(){
cin>>n>>m;
memset(d,INF,sizeof d);
memset(dist,INF,sizeof dist);
while(m--){
int a,b,w;
cin>>a>>b>>w;
d[a][b] = d[b][a] = min(d[a][b], w);
}
int ans = prim();
if(ans != INF){
cout<
Krustal算法
#include
#include
using namespace std;
const int N = 100010,M = 200020;
int s[N];
int root(int x){
if(x!=s[x])s[x] = root(s[x]);
return s[x];
}
int n,m;
struct edge{
int a,b,w;
bool operator<(const edge& W)const{
return w>n>>m;
for(int i=0;i>ed[i].a>>ed[i].b>>ed[i].w;
}
sort(ed,ed+m);
int res = 0, cnt =0;
for(int i=1;i<=n;i++)s[i]=i;
for(int i=0;i
二分图染色与匹配
判断二分图
#include
using namespace std;
const int N = 100010,M = 200020;
int h[N],e[M],ne[M],w[M],idx = 0;
void add(int a, int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int n,m;
int clr[N];
bool dfs(int u, int c){
clr[u] = c;
for(int i=h[u];i!=-1;i=ne[i]){
int j = e[i];
if(!clr[j]){
if(!dfs(j,3-c))return false;
}
else if(clr[j] == c)return false;
}
return true;
}
#include
int main(){
memset(h,-1,sizeof h);
int n,m;
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
add(a,b);add(b,a);
}
bool flg = true;
for(int i =1;i<=n;i++){
if(!clr[i]){
if(!dfs(i,1)){
flg = false;
break;
}
}
}
if(flg)cout<<"Yes"<
匈牙利算法求最大匹配
#include
#include
using std::memset;
using std::cin;
using std::cout;
using std::endl;
const int N = 510,M = 100010;
int h[N],e[M],ne[M],idx=0;
int match[N];
bool st[N];
void add(int a, int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool pair(int x){
for(int i=h[x];i!=-1;i = ne[i]){
int j = e[i];
if(!st[j]){
st[j] = true;
if(match[j] == 0 || pair(match[j])){
match[j] = x;
return true;
}
}
}
return false;
}
int main(){
memset(h,-1,sizeof h);
int n1,n2,m;
cin>>n1>>n2>>m;
while(m--){
int a,b;
cin>>a>>b;
add(a,b);
}
int res = 0;
for(int i=1;i<=n1;i++){
memset(st,false,sizeof st);
if(pair(i))res++;
}
cout<
字符串基础算法
字符串匹配
KMP
- 时间复杂度 \(O(m)\) \(m : 文本串长度\)
#include
using namespace std;
const int N=10010, M = 1000010;
//约定字符串下标从1开始
char s[M],p[N];
//next数组
int ne[N];
int m,n;
int main(){
cin>>m>>p+1>>n>>s+1;
//处理next数组
for(int i=2, j=0;i<=n;i++){
while(j && p[i] != p[j+1])j = ne[j];
if(p[i] == p[j+1])j++;
ne[i] = j;
}
// kmp匹配
for(int i=1,j=0;i <= m; i++){
while(j && s[i] != p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
//匹配结束,模式串整个跳转 j = ne[j]
if(j == n){
printf("%d ", i-n+1);
j = ne[j];
}
}
return 0;
}
字符串哈希
判断相等子串
- 时间复杂度 \(O(q)\) \(q:查询的数量\)
#include
using namespace std;
const int N = 100010, P= 13331; //p取131 或 13331都可
// 关键: 实现了自动模2^64
typedef unsigned long long ULL;
// p[i] p^i h[i] [0,i]的子串的哈希值
ULL p[N], h[N];
//获取子串的哈希值
ULL gH(int i ,int j){
return h[i-1]*p[j-i+1] - h[j];
}
int main(){
int n,m;
cin>>n>>m;
string s;
cin>>s;
p[0] = 1;
for(int i = 1;i<=s.size();i++){
p[i] = P*p[i-1];
h[i] = h[i-1]*P+s[i-1];
}
while(m--){
int i,j,k,l;
cin>>i>>j>>k>>l;
if(gH(i,j)==gH(k,l))cout<<"Yes"<
数论基础算法
质数
判断质数
- 时间复杂度 \(O(\sqrt n)\)
bool is_prime(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0)return false;
}
return true;
}
分解质因数
- 时间复杂度 最佳\(O(\log n)\) 最坏 \(O(\sqrt n)\)
void divide(int x){
for(int i=2;i<= x/i;i++){
if(x%i == 0){
int t = 0;
while(x%i == 0){
x/=i;
++s;
}
//质因数 次数
printf("%d %d\n",i,t);
}
}
if(x > 1)printf("%d %d\n",x , 1);
}
筛质数
- 时间复杂度 \(O(n \log \log n)\)
bool p[N];
int prime[N],cnt;
int s(int n){
for(int i=2;i<=n;i++){
if(!p[i]){
prime[cnt++] = i;
for(int j=i+i; j <= n;j+=i)p[j] = true;
}
}
}
- 时间复杂度 \(O(n\log \log n)\)
bool p[N];
int prime[N],cnt;
bool ai(int n){
for(int i=2;i <= n;i++){
if(!p[i]) prime[cnt++] = i;
for(int j =0 ; prime[j] <= n/i; j++){
p[prime[j]*i] = true;
//i%prime[j]==0 -> prime[j]一定是i的最小质因子
if(i % prime[j] == 0)break;
}
}
}
约数
所有约数
- 时间复杂度 \(O(\sqrt{n}+\log{n}\log{\log{n}})\)
vector diver(int x){
vector ans;
for(int i=1;i<= x/i;i++){
if(x%i == 0){
ans.push_back(i);
if(i != x/i)ans.push_back(x/i);
}
}
sort(ans.begin(), ans.end());
return ans;
}
约数个数
- 时间复杂度 最坏 \(O(\sqrt n)\) 最佳 \(O(\log n)\)
int diver_cnt(int x){
int res = 1;
for(int i=2;i<=x;i++){
if(x%i == 0){
int alpha=0;
while(x%i ==0){
x/=i;
alpha++;
}
res*= (alpha+1);
}
}
if(x>1)res*=2;
return res;
}
约数之和
typedef unsigned long long ULL;
const int mod = 100007;
ULL diver_sum(int x){
ULL res = 1;
for(int i=2;i<=x;i++){
if(x%i == 0){
int alpha=0;
while(x%i ==0){
x/=i;
alpha++;
}
//秦九昭算法 1+q+..+q^n = 1+q(1+q(...))
ULL t = 1
for(int j =0;j1)res = res*(x+1)%mod;
return res;
}
最大公约数与最小公倍数
// 最大公约数
int gcd(int a, int b){
return b? gcd(b,a%b):a;
}
//最小公倍数
int lcm(int a, int b){
return a*b/gcd(a,b);
}
快速幂
- 时间复杂度 \(O(\log k)\) \(k:幂\)
typedef long long LL;
// a^k % p
LL quick_pow(int a, int k, int p){
int res = 1;
while(k){
if(k & 1) res = (LL)res*a % p;
k>>=1;
a = (LL) a*a % p;
}
return res;
}
快速积
- 时间复杂度 \(O(\log n)\) \(n: 乘积项的任意一个\)
typedef unsigned long long ULL;
// a*b % p
ULL quick_add(ULL a, ULL b, ULL p){
ULL res = 0;
while(b){
if(b & 1)r = (r + a)%p;
b >>= 1;
a = a*2 % p;
}
return res;
}
求组合数算法
递推公式法
- 时间复杂度 \(O(n^2)\)
const int mod = 1e9+7;
int C[N][N];//C[a][b]
void init(){
for(int i=0;i
阶乘公式法
- 时间复杂度 \(O(n\log n)\)
typedef long long LL;
const int mod = 1e9+7;//模是质数
int fact[N],infact[N];
int quick_pow(int a,int k, int p);
void init(){
fact[0] = infact[0] = 1;
for(int i=1;i
经典动态规划模型
序列模型
最长上升子序列
- 时间复杂度 \(O(n^2)\)
- 状态定义: \(f(i) : 以第i个数结尾的最长上升子序列的长度\)
- 状态转移: \(f(i) = \max_{j:1\to i-1} : f(j)+1\)
#include
using namespace std;
const int N = 1010;
// p 序列 f[i] 以第i个数为结尾的最长上升子序列的长度
int p[N],f[N];
int main(){
int n;
cin>>n;
for(int i =1;i<=n;i++){
cin>>p[i];
}
//初始化
for(int i = 1;i<=n;i++)f[i] = 1;
for(int i = 2;i <= n;i++){
for(int j = 1;j
- 时间复杂度 \(O(n\log n)\)
#include
#include
using namespace std;
const int N = 100010;
int p[N],f[N];
int main(){
int n;
cin>>n;
for(int i = 0;i>p[i];
int cnt = 1;
f[0] = p[0];
for(int i = 1;i
最小覆盖问题
结论:最长上升子序列的最小覆盖数等于最长不上升子序列的长度
最长公共子序列
- 状态定义 \(f(i,j) : 公共序列的末尾元素是A序列的第i个数和B序列的第j个数时的最长长度\)
#include
using namespace std;
const int N = 1010;
char A[N],B[N];
int f[N][N];
int main(){
int n,m;
cin>>n>>m;
for(int i = 1;i<=n;i++)cin>>A[i];
for(int j = 1;j<=m;j++)cin>>B[j];
// 分类 : 序列末尾是A[i]\是B[j]\是A[i]和B[j]\都不是
for(int i = 1;i<=n;i++)
for(int j = 1;j<=m;j++)
if(A[i] == B[j])f[i][j] = f[i-1][j-1]+1;
else{
f[i][j] = max(max(f[i-1][j],f[i-1][j-1]),f[i][j-1]);
}
cout<
背包模型
01背包
- 时间复杂度 \(O(nv)\) \(n:物品数量\) $ v: 背包体积$
#include
using namespace std;
const int N = 1010;
int dp[N],w[N],v[N];
int slove(int n, int mv){
for(int i = 1; i<= n; i++){
for(int j = mv;j>=v[i];j--){
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
}
return dp[mv];
}
int main(){
int n,mv;
cin>>n>>mv;
for(int i = 1;i<=n;i++)cin>>v[i]>>w[i];
cout<
完全背包
#include
using namespace std;
const int N = 1010;
int dp[N],w[N],v[N];
int main(){
int n,mv;
cin>>n>>mv;
for(int i = 1; i<=n ; i++)cin>>v[i]>>w[i];
for(int i = 1;i<=n;i++)
for(int j = v[i];j<=mv;j++)
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
cout<
多重背包
#include
using namespace std;
const int N = 100010;
int dp[N],w[N],v[N];
int main(){
int n,mv,idx=0;
cin>>n>>mv;
for(int i = 1;i<=n;i++){
int a,b,s;
cin>>a>>b>>s;
int d = 1;
while(s>=d){
idx++;
w[idx] = b*d;
v[idx] = a*d;
s-=d;
d*=2;
}
if(s>0){
idx++;
w[idx] = b*s;
v[idx] = a*s;
}
}
for(int i = 1;i<=idx;i++)for(int j = mv;j>=v[i];j--)dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
cout<
单调队列优化
#include
using namespace std;
const int N = 1010, M = 20010;
int n, m;
int v[N], w[N], s[N];
int f[2][M];
int q[M];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++ i) cin >> v[i] >> w[i] >> s[i];
for (int i = 1; i <= n; ++ i)
{
for (int r = 0; r < v[i]; ++ r)
{
int hh = 0, tt = -1;
for (int j = r; j <= m; j += v[i])
{
while (hh <= tt && j - q[hh] > s[i] * v[i]) hh ++ ;
while (hh <= tt && f[(i - 1) & 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[(i - 1) & 1][j]) -- tt;
q[ ++ tt] = j;
f[i & 1][j] = f[(i - 1) & 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
}
}
}
cout << f[n & 1][m] << endl;
return 0;
}
分组背包
#include
using namespace std;
const int N = 150;
int dp[N],v[N][N],w[N][N],s[N];
int slove(int n, int mv){
for(int i = 1;i<=n;i++)
for(int j = mv; j>=0; j--)
for(int k = 0;k<=s[i];k++)
if(j>=v[i][k])dp[j] = max(dp[j-v[i][k]]+w[i][k], dp[j]);
return dp[mv];
}
int main(){
int n,mv;
cin>>n>>mv;
for(int i =1;i<=n;i++){
cin>>s[i];
for(int j = 1;j<=s[i];j++){
cin>>v[i][j]>>w[i][j];
}
}
slove(n,mv);
cout<
数位统计
- 时间复杂度 \(G函数的时间复杂度\)
[a,b]间满足性质K的数的个数 -> G(x)是 [s,x]间满足性质K的数的个数 [a,b] = G(b) - G(a-1)
cnt G( x ) :
将x拆分为每一位
统计性质
返回个数
return cnt