ACM板子——【动态规划问题】全收录

一、背包问题

【1.1】01背包

1 for(int i=1;i<=n;i++)
2     for(int j=m;j>=v[i];j--)
3         f[j]=max(f[j],f[j-v[i]]+w[i]);
View Code

【1.2】完全背包

复制代码
 1 #include<iostream>//用一维数据f[j]只记录一行数据,让j值顺序循环,顺序更新f[j]值
 2 using namespace std;
 3 const int N=1010;
 4 int f[N],v[N],w[N];
 5 int main()
 6 {//对于前i件物品,背包容量为j-w[i]时可能已经放入了第i件物品,容量为j时还可以再放入第i件物品,
 7     int n,m;//所以用f[i][j-w[i]]更新f[i][j]
 8     cin>>n>>m;
 9     for(int i=1;i<=n;i++)
10         cin>>v[i]>>w[i];
11     for(int i=1;i<=n;i++)
12         for(int j=v[i];j<=m;j++)
13             f[j]=max(f[j],f[j-v[i]]+w[i]);
14     cout<<f[m]<<endl;
15     return 0;
16 }
View Code
复制代码

【1.3】多重背包问题1

复制代码
 1 #include<iostream>//多重背包可以转化为01背包
 2 using namespace std;//01背包:第i种物品可以取0件、1件
 3 const int N=110;//多重背包:第i种物品可以取0件、1件、2件、si件
 4 int f[N];//求解:把第i种物品换成si件01背包种的物品,每件物品的体积为k*vi,价值为k*wi
 5 int main()
 6 {
 7     int n,m;
 8     cin>>n>>m;
 9     for(int i=0;i<n;i++)
10     {
11         int v,w,s;
12         cin>>v>>w>>s;
13         for(int j=m;j>=v;j--)
14             for(int k=1;k<=s&&k*v<=j;k++)
15                 f[j]=max(f[j],f[j-k*v]+k*w);
16     }
17     cout<<f[m];
18     return 0;
19 }
View Code
复制代码

【1.4】多重背包问题2(数据更大的情况下,二进制优化)

复制代码
 1 #include<iostream>
 2 #include<algorithm>
 3 #define endl '\n'
 4 #define ll long long
 5 using namespace std;
 6 const int N=3e4+10;
 7 int f[N],v[N],w[N];
 8 int main()
 9 {
10     int n,m;
11     cin>>n>>m;
12     int cnt=0;
13     for(int i=1;i<=n;i++)
14     {
15         int a,b,s;
16         cin>>a>>b>>s;
17         int k=1;
18         while(k<=s)
19         {
20             cnt++;
21             v[cnt]=a*k;
22             w[cnt]=b*k;
23             s-=k;
24             k*=2;
25         }
26         if(s>0)
27         {
28             cnt++;
29             v[cnt]=a*s;
30             w[cnt]=b*s;
31         }        
32     }
33     n=cnt;
34     for(int i=1;i<=n;i++)
35         for(int j=m;j>=v[i];j--)
36             f[j]=max(f[j],f[j-v[i]]+w[i]);
37     cout<<f[m]<<endl;
38     return 0;    
39 }
View Code
复制代码

(vector数组写法)

复制代码
 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 const int N=2010;
 5 int f[N];
 6 struct Good{
 7     int v,w;
 8 };
 9 int main()
10 {
11     int n,m;
12     cin>>n>>m;
13     vector<Good> goods;
14     for(int i=1;i<=n;i++)
15     {
16         int v,w,s;
17         cin>>v>>w>>s;
18         for(int k=1;k<=s;k*=2)
19         {
20             goods.push_back({v*k,w*k});
21             s-=k;
22         }
23         if(s)
24             goods.push_back({v*s,w*s});
25     }
26     for(auto good:goods)
27         for(int j=m;j>=good.v;j--)
28             f[j]=max(f[j],f[j-good.v]+good.w);
29     cout<<f[m]<<endl;
30     return 0;
31 }
View Code
复制代码

【1.5】多重背包问题3(单调队列优化)

【1.6】混合背包问题

【1.7】二维费用背包问题

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 const int N=110;
 4 int f[N][N];
 5 int main()
 6 {
 7     int n,v,m;
 8     cin>>n>>v>>m;
 9     for(int i=0;i<n;i++)
10     {
11         int a,b,c;
12         cin>>a>>b>>c;
13         for(int j=v;j>=a;j--)
14             for(int k=m;k>=b;k--)
15                 f[j][k]=max(f[j][k],f[j-a][k-b]+c);
16     }
17     cout<<f[v][m];
18     return 0;
19 }
View Code
复制代码

 【1.8】分组背包问题

复制代码
 1 /*
 2 f[j]=max(f[j],f[j-v[0]]+w[0],f[j-v[1]]+w[1],f[j-v[2]],w[2],...)
 3 */
 4 #include<iostream>
 5 using namespace std;
 6 const int N=110;
 7 int f[N][N];
 8 int main()
 9 {
10     int n,v,m;
11     cin>>n>>v>>m;
12     for(int i=0;i<n;i++)
13     {
14         int a,b,c;
15         cin>>a>>b>>c;
16         for(int j=v;j>=a;j--)
17             for(int k=m;k>=b;k--)
18                 f[j][k]=max(f[j][k],f[j-a][k-b]+c);
19     }
20     cout<<f[v][m];
21     return 0;
22 }
View Code
复制代码

 

二、线性DP

【2.1】数字金字塔

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 const int N=510;
 4 int f[N][N];
 5 int main()
 6 {
 7     int n;
 8     cin>>n;
 9     for(int i=1;i<=n;i++)
10         for(int j=1;j<=i;j++)
11             cin>>f[i][j];
12     for(int i=n-1;i>=1;i--)
13         for(int j=1;j<=i;j++)
14             f[i][j]+=max(f[i+1][j],f[i+1][j+1]);
15     cout<<f[1][1]<<endl;
16     return 0;
17 }
View Code
复制代码

 

【2.3】最长上升子序列

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 const int N=1e5+10;
 4 int a[N],f[N];
 5 int main()
 6 {
 7     int n;
 8     cin>>n;
 9     for(int i=1;i<=n;i++) cin>>a[i];
10     for(int i=1;i<=n;i++)
11     {
12         f[i]=1;
13         for(int j=1;j<i;j++)
14             if(a[j]<a[i])
15                 f[i]=max(f[i],f[j]+1);
16     }
17     int ans=0;
18     for(int i=1;i<=n;i++) ans=max(ans,f[i]);
19     cout<<ans<<endl;
20     return 0;
21 }
View Code
复制代码

【2.3.1】Array Division

复制代码
 1 int main()
 2 {
 3     cin.tie(0)->sync_with_stdio(0);
 4     int t;
 5     cin>>t;
 6     while(t--)
 7     {
 8         int n;
 9         cin>>n;
10         for(int i=1;i<=n;i++)
11         {
12             cin>>a[i];
13             a[i]+=a[i-1];
14         }
15         for(int i=1;i<=n;i++)
16         {
17             cin>>b[i];
18             b[i]+=b[i-1];
19         }
20         memset(f,-2,sizeof(f));
21         f[0]=0;
22         for(int i=1;i<=n;i++)
23             for(int j=0;j<i;j++)
24                 if(a[i]-a[j]>=b[i]-b[j])
25                     f[i]=max(f[i],f[j]+1);
26         if(f[n]<0)f[n]=-1;
27         cout<<f[n]<<endl;
28     }
29     return 0;
30 }
View Code
复制代码

 

【2.4】最长上升子序列2(二分查找优化)

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=100010;
 4 int n,a[N],q[N];
 5 int main()//时间复杂度NlogN
 6 {
 7     int n;
 8     cin>>n;
 9     for(int i=0;i<n;i++) cin>>a[i];
10     int len=0;//存的是最大长度,一开始为0
11     q[0]=-2e9;//为了保证数组中,小于某个数的数一定存在
12     for(int i=0;i<n;i++)
13     {
14         int l=0,r=len;
15         while(l<r)//我们要找到小于a[i]的最大的数
16         {
17             int mid=l+r+1>>1;//因为我们算的是l=mid,所以要上取整
18             if(q[mid]<a[i]) l=mid;//所以我们要找到的答案一定在mid的右边
19             else r=mid-1;
20         }
21         len=max(len,r+1);//接完a[i]后长度会加1
22         q[r+1]=a[i];//q[r+1]一定大于等于a[i]
23     }
24     cout<<len;
25     return 0;
26 }
View Code
复制代码

第二种写法

复制代码
 1 //如果我们把内层循环改为二分查找,就能把内层查找时间降为logn
 2 //时间复杂度降为nlogn
 3 //但是,二分查找的前提是有序序列,故增加一个b数组,用来记录上升子序列
 4 #include<iostream>
 5 using namespace std;
 6 const int N=1e5+10;
 7 int a[N],b[N],len;
 8 int find(int x)
 9 {//二分查找第一个大于等于x的位置
10     int l=1,r=len,mid;
11     while(l<=r)
12     {
13         mid=(l+r)/2;
14         if(x>b[mid])l=mid+1;
15         else r=mid-1;
16     }
17     return l;
18 }
19 int main()
20 {
21     int n;
22     cin>>n;
23     for(int i=1;i<=n;i++)
24         cin>>a[i];
25     len=1;
26     b[1]=a[1];
27     //动态更新b数组
28     for(int i=2;i<=n;i++)
29     {
30         if(a[i]>b[len])//大于则添加
31             b[++len]=a[i];
32         else//小于等于则替换
33         {
34             int j=find(a[i]);//用a[i]替换掉b数据中第一个大于或者等于a[i]的元素
35             b[j]=a[i];//这样的上升子序列更有潜力
36         }//这样会使b的上升子序列的结尾元素更小,从而越有利于续接其它元素
37     }
38     cout<<len<<endl;
39     return 0;
40 }
View Code
复制代码

【2.5】最长公共子序列

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1010;
 4 char a[N],b[N];
 5 int f[N][N];
 6 int main()
 7 {
 8     int n,m;
 9     cin>>n>>m>>a+1>>b+1;
10     for(int i=1;i<=n;i++)
11     {
12         for(int j=1;j<=m;j++)
13         {
14             f[i][j]=max(f[i-1][j],f[i][j-1]);
15             if(a[i]==b[j])
16                 f[i][j]=max(f[i][j],f[i-1][j-1]+1);
17         }
18     }
19     cout<<f[n][m]<<endl;
20     return 0;
21 }
View Code
复制代码

【2.6】最短编辑距离

复制代码
 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=1010;
 5 char a[N],b[N];
 6 int n,m;
 7 int f[N][N];
 8 int main()
 9 {
10     cin>>n>>a+1>>m>>b+1;
11     for(int i=0;i<=n;i++) f[i][0]=i;
12     for(int i=0;i<=m;i++) f[0][i]=i;
13     for(int i=1;i<=n;i++)
14         for(int j=1;j<=m;j++)
15         {
16             f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
17             if(a[i]==b[j])
18                 f[i][j]=min(f[i-1][j-1],f[i][j]);
19             else
20                 f[i][j]=min(f[i-1][j-1]+1,f[i][j]);
21         }
22     cout<<f[n][m];
23     return 0;
24 }
View Code
复制代码

【2.7】编辑距离

复制代码
 1 #include<iostream>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 const int N=15,M=1010;
 6 int n,m;
 7 int f[N][N];
 8 char str[M][N];
 9 int edit_distance(char a[],char b[])
10 {
11     int la=strlen(a+1),lb=strlen(b+1);
12     for(int i=0;i<=lb;i++)f[0][i]=i;
13     for(int i=0;i<=la;i++)f[i][0]=i;
14     for(int i=1;i<=la;i++)
15         for(int j=1;j<=lb;j++)
16         {
17             f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
18             f[i][j]=min(f[i][j],f[i-1][j-1]+(a[i]!=b[j]));
19         }
20     return f[la][lb];
21 }
22 int main()
23 {
24     cin>>n>>m;
25     for(int i=0;i<n;i++)cin>>str[i]+1;
26     while(m--)
27     {
28         char s[N];
29         int limit;
30         cin>>s+1>>limit;
31         int res=0;
32         for(int i=0;i<n;i++)
33             if(edit_distance(str[i],s)<=limit)
34                 res++;
35         cout<<res<<endl;
36     }
37     return 0;
38 }
View Code
复制代码

 

三、区间DP

【3.1】石子合并

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 const int N=310;
 4 int n,s[N],f[N][N];
 5 int main()
 6 {
 7     cin>>n;
 8     for(int i=1;i<=n;i++)cin>>s[i],s[i]+=s[i-1];
 9     for(int len=2;len<=n;len++)//区间长度为1不用枚举
10         for(int i=1;i+len-1<=n;i++)
11         {
12             int j=i+len-1;
13             f[i][j]=1e8;
14             for(int k=i;k<j;k++)
15                 f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
16         }
17     cout<<f[1][n]<<endl;
18 }
View Code
复制代码

【3.1.1】Doin's Time

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int mod=1000003;
 4 const int N=310;
 5 long long f[N][N],s[N],a[N][N];
 6 int main()
 7 {
 8     int n;
 9     cin>>n;
10     for(int i=1;i<=n;i++)
11         cin>>s[i];
12     for(int i=1;i<=n;i++)
13     {
14         long long now=1;
15         for(int j=i;j<=n;j++)
16         {
17             now=now*s[j]%mod;
18             a[i][j]=now;
19         }
20     }
21     for(int len=2;len<=n;len++)
22         for(int i=1;i+len-1<=n;i++)
23         {
24             int j=i+len-1;
25             for(int k=i;k<j;k++)
26             {
27                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+(a[i][k]-a[k+1][j])*(a[i][k]-a[k+1][j]));
28             }
29         }   
30     cout<<f[1][n]<<endl;
31 }
View Code
复制代码

四、计数类DP

【4.1】整数划分

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1010,mod=1e9+7;
 4 int n,f[N];//每种物品可以用无限次,完全背包问题
 5 int main()
 6 {
 7 
 8 /*
 9 f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
10 f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
11 因此 f[i][j]=f[i−1][j]+f[i][j−i];f[i][j]=f[i−1][j]+f[i][j−i]; (这一步类似完全背包的推导
12 */
13     cin>>n;
14     f[0]=1;
15     for(int i=1;i<=n;i++)
16         for(int j=i;j<=n;j++)
17             f[j]=(f[j]+f[j-i])%mod;
18     cout<<f[n]<<endl;
19     return 0;
20 }
View Code
复制代码

五、数位统计DP

【5.1】计数问题

六、状态压缩DP

【6.1】蒙德里安的梦想

复制代码
 1 /*我们考虑按列摆放,某列的各行用0或1表示摆放状态
 2 如果某行是1,表示横放,并且向下一列伸出
 3 如果某行是0,表示竖放,或者由前一列伸出
 4 状态表示:f[i][j]表示摆放第i列,状态为j时的方案数
 5 状态转移:f[i-1][k]->f[i][j]
 6 答案:f[m][0]表示已经摆完了m列,这一列不向下一列伸出
 7 */
 8 #include<bits/stdc++.h>
 9 using namespace std;
10 int f[12][1<<11];
11 bool st[1<<11];
12 int main()
13 {
14     int m,n;
15     while(cin>>n>>m,m||n)
16     {
17         memset(f,0,sizeof f);
18         f[0][0]=1;//f[0][0]=1,表示第0列不放矩形,是合法的状态,所以是1,其它为0
19         for(int i=0;i<1<<n;i++)//c从0枚举到2的n次方-1
20         {
21             st[i]=true;//先假设i是真的
22             int cnt=0;//记录合并列中连续0的个数
23             for(int j=0;j<n;j++)
24             {
25                 if(i>>j&1)//如果是1
26                 {
27                     if(cnt&1)//如果连续0的个数是奇数
28                     {
29                         st[i]=false;//记录i不合法
30                         break;
31                     }
32                 }
33                 else cnt++;//如果是0,记录0的个数
34             }
35             if(cnt&1)st[i]=false;//处理高位0的个数
36         }
37         for(int i=1;i<=m;i++)//阶段:枚举第i列的状态
38             for(int j=0;j<1<<n;j++)//状态:枚举第i列的状态j
39                 for(int k=0;k<1<<n;k++)//状态:枚举第i-1列的状态k
40                     //两列状态兼容,不出现重叠1,不出现连续的奇数个0
41                     if((j&k)==0&&st[j|k])
42                         f[i][j]+=f[i-1][k];//前一列兼容状态的方案数之和
43         cout<<f[m][0]<<endl;
44     }
45     /*
46     假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
47     A = 0011 1100
48     B = 0000 1101
49     -----------------
50     A&B = 0000 1100
51     A|B = 0011 1101
52     A^B = 0011 0001
53     ~A  = 1100 0011
54 */
55     return 0;
56 }
View Code
复制代码

最短Hamilton路径

 

七、树形DP

【7.1】没有上司的舞会

复制代码
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int N = 6010;
 6 int n;
 7 int happy[N]; //每个职工的高兴度
 8 int f[N][2]; 
 9 int e[N],ne[N],h[N],idx; //链表,用来模拟建一个树
10 bool has_father[N]; //判断当前节点是否有父节点
11 void add(int a,int b){ //把a插入树中
12     e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
13 }
14 void dfs(int u){ //开始求解题目
15     f[u][1] = happy[u]; //如果选当前节点u,就可以把f[u,1]先怼上他的高兴度
16     for(int i = h[u];i!=-1;i = ne[i]){ //遍历树
17         int j = e[i];
18         dfs(j); //回溯
19         //状态转移部分,上面有详细讲解~
20         f[u][0] += max(f[j][1],f[j][0]);
21         f[u][1] += f[j][0];
22     }
23 }
24 int main(){
25     scanf("%d",&n);
26     for(int i = 1;i <= n;i ++) scanf("%d",&happy[i]); //输入每个人的高兴度
27     memset(h,-1,sizeof h); //把h都赋值为-1
28     for(int i = 1;i < n;i ++){
29         int a,b; //对应题目中的L,K,表示b是a的上司
30         scanf("%d%d",&a,&b); 
31         has_father[a] = true; //说明a他有爸爸(划掉)上司
32         add(b,a); //把a加入到b的后面
33     }
34     int root = 1; //用来找根节点
35     while(has_father[root]) root ++; //找根节点
36     dfs(root); //从根节点开始搜索
37     printf("%d\n",max(f[root][0],f[root][1])); //输出不选根节点与选根节点的最大值
38     return 0;
39 }
View Code
复制代码

八、记忆搜索

【8.1】滑雪

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=310;
 4 int n,m,h[N][N],f[N][N];
 5 int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
 6 int dp(int x,int y)
 7 {
 8     int &v=f[x][y];
 9     if(v!=-1) return v;
10 
11     v=1;
12     for(int i=0;i<4;i++)
13     {
14         int a=x+dx[i],b=y+dy[i];
15         if(a>=1&&a<=n&&b>=1&&b<=m&&h[a][b]<h[x][y])
16             v=max(v,dp(a,b)+1);
17     }
18     return v;
19 }
20 int main()
21 {
22     cin>>n>>m;
23     for(int i=1;i<=n;i++)
24         for(int j=1;j<=m;j++)
25             cin>>h[i][j];
26     memset(f,-1,sizeof f);
27     int res=0;
28     for(int i=1;i<=n;i++)
29         for(int j=1;j<=m;j++)
30             res=max(res,dp(i,j));
31     cout<<res<<endl;
32 }
View Code
复制代码

 

posted @   may0113  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示