///关于下标问题,当在计算时运用到i-1的时候,可以使用i从1开始,就没有越界的风险 ///如果没有,一般从0开始比价好; 1.要想明白动态规划路线 ->第一步写出动态集合,第二步开始动态计算; 1-1 0-1背包问题: #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m;//物品数量,最多载重; int v[N],w[N];//物体的价值,重量; int dp[N][N];//二维数组:表示存放第几个物品时,此时的总重量; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) { for(int j=0;j<=m;j++)//重量从0-m; { dp[i][j]=dp[i-1][j]; if(j>=w[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]); } } cout<<dp[n][m]<<endl; return 0; } 1-1 0-1背包优化:一维数组 #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m;//物品数量,最多载重; int v[N],w[N];//物体的价值,重量; int dp[N];//二维数组:表示存放第几个物品时,此时的总重量; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--)//重量从0-m; dp[j]=max(dp[j],dp[j-w[i]]+v[i]); cout<<dp[m]<<endl; return 0; } 1-3 0-1背包输出背包 ///难点在于如何输出将哪个物品装入背包,可利用回溯法进行实现 ///更加考研对背包和动态规划的理解 #include<bits/stdc++.h> using namespace std; const int N=1005; int n,m; int v[N],w[N],f[N][N],x[N];///x[i]标记用过的背包; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]; for(int i=1;i<=n;i++) cin>>w[i]; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) { f[i][j]=f[i-1][j]; if(j>=w[i]) f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);///普通0-1背包; /// 每一个j都对应一个状态;后面回溯会根据这个状态来进行判断; } cout<<"Optimal value is"<<endl; cout<<f[n][m]<<endl; for(int i=n;i>=1;i--)///利用回溯,从后向前走; { if(f[i][m]!=f[i-1][m])///如果两者相等,也就是在i背包和i-1背包之间没有装任何物品; { m-=w[i]; x[i]=1; } } for(int i=1;i<=n;i++) cout<<x[i]<<" "; return 0; } 2-1 完全背包 与0-1背包不同,完全背包里物品可以多次使用,但大体思路相同; #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m; int v[N],w[N]; int dp[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k*w[i]<=m;k++) dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]+k*v[i]); cout<<dp[n][m]<<endl; return 0; } 2-2 完全背包优化: 1.根据分析,可以发现在循环时,如果是dp[i][j-w[i]]开头的话,后续的循环与dp[i][j]对比 两者的关系是dp[i][j]=dp[i][j-w[i]]+v[i]; 故我们可以少一层k循环; 2.利用0-1背包思路,优化成一维数组. #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m; int v[N],w[N]; int dp[N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) for(int j=w[i];j<=m;j++)///这里不需要逆序,因为0-1是看i-1的状态,而完全背包是i状态,不需要改变. dp[j]=max(dp[j],dp[j-w[i]]+v[i]); cout<<dp[m]<<endl; return 0; } ///看好0-1背包和完全背包问题非常的相似,想清楚这两者只差一行代码的区别在哪!!!! 3 多重背包问题-暴力 本质与完全背包问题没有区别,只是k循环最多循环到每个物品最高限制次,即s[i]次; #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m; int v[N],w[N]; int dp[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=s[i]&&k*v[i]<=j;k++)//就多出来一个判断条件; dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]+k*v[i]); cout<<dp[n][m]<<endl; return 0; } 3-2 多重背包问题-优化 详解://https://blog.csdn.net/weixin_72060925/article/details/128404378?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128404378-blog-109363826.235%5Ev30%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-128404378-blog-109363826.235%5Ev30%5Epc_relevant_default_base3&utm_relevant_index=2 #include<bits/stdc++.h> using namespace std; const int N=25000,M=1010; int n,m; int v[N],w[N]; int dp[N]; int main() { cin>>n>>m; int cnt=0; for(int i=1;i<=n;i++) { int a,b,s; cin>>a>>b>>s; int k=1; while(k<=s)//使用二进制计数法,例如s=200,那么我们的k就可以循环到64,此时可以表示出1-128的所有数字 { //那么剩下的72个怎么表示呢,剩下的72个 cnt++; w[cnt]=k*a; v[cnt]=k*b; s-=k; k*=2; } if(s>0) { cnt++; w[cnt]=a*s; v[cnt]=b*s; } } n=cnt;//更新n的值,接下来就当0-1背包问题做就可以; for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); cout<<dp[m]<<endl; return 0; } 4-1 多组背包 #include<bits/stdc++.h> using namespace std; const int N=110; int n,m; int w[N][N],v[N][N],s[N]; int dp[N]; int main() { cin>>n>>m; for(int i=0;i<n;i++) { cin>>s[i]; for(int j=0;j<s[i];j++) cin>>w[i][j]>>v[i][j]; } for(int i=0;i<n;i++) for(int j=m;j>0;j--) for(int k=0;k<s[i];k++) if(j>=w[i][k]) dp[j]=max(dp[j],dp[j-w[i][k]]+v[i][k]);//还是老方法,就是多了重循环哈 cout<<dp[m]<<endl; return 0; } 5- 依赖背包,存在主附,要买附就要现有主 //P1064 [NOIP2006 提高组] 金明的预算方案 //链接:https://www.luogu.com.cn/problem/P1064 ///当时做这个题时,满脑子都是思路,一遍遍确认一遍遍不对,最后的大体思路是对了 ///但没想到分情况讨论. ///依赖背包在附件少的情况下完全可以分情况讨论,等我再看看算法,应该有个通用方法. #include<iostream> using namespace std; int m,n,mw[33333],mv[33333],fw[33333][3],fv[33333][3],f[33333],v,p,q; //mw主件重量,mv主件价值,fw主件对应的附件重量,fv主副价值,n总重量,m总个数 int main() { cin>>n>>m; for(int i=1;i<=m;i++){ cin>>v>>p>>q; if(!q){//如果是主件 mw[i]=v;//主件重量 mv[i]=v*p;//主件价值与重量乘积 } else{//如果是附件 fw[q][0]++;//记录主件的附件个数(只记录在fw就行,fv那里没用 fw[q][fw[q][0]]=v;//主件的个数是用来确定该附件应该填在第一个还是第二个格子里 fv[q][fw[q][0]]=v*p;//(是第一个还是第二个附件) } } for(int i=1;i<=m;i++) for(int j=n;j>=mw[i];j--){//01背包模板 //每一个if的前提是背包能不能装下该物品 //情况1:只要主件 和啥都不要比较 f[j]=max(f[j],f[j-mw[i]]+mv[i]); //情况2:主件和附件1 和上面选出的较大值比较 if(j>=mw[i]+fw[i][1])f[j]=max(f[j],f[j-mw[i]-fw[i][1]]+mv[i]+fv[i][1]); //情况3:主件和附件2 和上面选出的较大值比较 if(j>=mw[i]+fw[i][2])f[j]=max(f[j],f[j-mw[i]-fw[i][2]]+mv[i]+fv[i][2]); //情况4:都要 if(j>=mw[i]+fw[i][1]+fw[i][2]) f[j]=max(f[j],f[j-mw[i]-fw[i][1]-fw[i][2]]+mv[i]+fv[i][1]+fv[i][2]); } //输出在价值为n时能得到的最大值 cout<<f[n]<<endl; return 0; } ///线性dp 6-数字三角形 #include<bits/stdc++.h> #define INF -1e9 using namespace std; const int N=505; int n,a[N][N],f[N][N]; int main() { cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>a[i][j]; for(int i=0;i<=n+1;i++) for(int j=0;j<=i+1;j++) f[i][j]=INF; f[1][1]=a[1][1];//一定要记得从起点开始初始化,不然会变成负无穷 for(int i=2;i<=n;i++)//一定要从第二层开始,如果从第一层开始,第一次会访问到负无穷区域; for(int j=1;j<=i;j++) f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]); int res=INF; for(int i=1;i<=n;i++) res=max(res,f[n][i]); cout<<res; return 0; } 7-1 最长上升/下降 子序列 #include<bits/stdc++.h> using namespace std; const int N=1010; int n; int a[N],f[N]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) { f[i]=1;//每个i位置初始都是1,没循环之前就a[i]一个数字 for(int j=1;j<i;j++) if(a[i]>a[j])//如果i位置比之前的大 f[i]=max(f[i],f[j]+1); } int res=-1; for(int i=1;i<=n;i++) res=max(res,f[i]); cout<<res; } 7-2 最长子序列输出顺序 #include<bits/stdc++.h> using namespace std; const int N=1010; int n; int a[N],g[N],f[N]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) { f[i]=1; g[i]=0; for(int j=1;j<i;j++) if(f[i]<f[j]+1) { f[i]=f[j]+1; g[i]=j; } } int res=0,k=0; for(int i=1;i<=n;i++) if(f[i]>res) { res=f[i]; k=i; } cout<<res<<endl; for(int i=0,len=f[k];i<len;i++) { cout<<a[k]<<' '; k=g[k]; } return 0; } 7-3 最长上升子序列优化 #include<bits/stdc++.h> using namespace std; const int N=100010; int res=0,x,a[N],q[N],q1[N],len=0; int main() { while(cin>>x) a[res++]=x; q[0]=2e9; q1[0]=-2e9; for(int i=0;i<res;i++) { if(q[len]>=a[i]) q[++len]=a[i]; else { int l=0,r=len; while(l<=r) { int mid=l+r>>1; if(q[mid]<a[i]) r=mid-1; else l=mid+1; } q[l]=a[i]; } } cout<<len<<endl;//最长不上升 len=0; for(int i=0;i<res;i++) { if(q1[len]<a[i]) q1[++len]=a[i]; else { int l=0,r=len; while(l<=r) { int mid=l+r>>1; if(q1[mid]>=a[i]) r=mid-1; else l=mid+1; } q1[l]=a[i]; } } cout<<len;//最长上升 return 0; } 8-1 最长公共子序列 #include<bits/stdc++.h> using namespace std; const int N=1010; int n,m; char a[N],b[N]; int f[N][N]; int main() { cin>>n>>m; cin>>a+1>>b+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { f[i][j]=max(f[i-1][j],f[i][j-1]);///状态转移说简单些就是做选择,比如说这个问题,是求 s1 和 s2 的最长公共子序列,不妨称这个子序列为 lcs. ///那么对于 s1 和 s2 中的每个字符,有什么选择?很简单,两种选择,要么在 lcs 中,要么不在。 ///如果某个字符应该在 lcs 中,那么这个字符肯定同时存在于 s1 和 s2 中; ///如果 s1[i]==s2[j],那么这个字符一定在 lcs 中;否则的话,s1[i] 和 s2[j] 这两个字符至少有一个不在 lcs 中,需要丢弃一个. ///保留一个的原因是,i从小到大遍历,i后面有可能会出现与这时保留相同的字节 if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1); } cout<<f[n][m]; } 8-2 最长公共子序列优化: https://www.luogu.com.cn/problem/P1439 /// 运用离散化来解决问题; ///我们可以以第一个串为标准,用第二个串来匹配第一个串,看能匹配多少,所以,其实第一个串的每个数字其实影响不大,只有知道它对应了第二串的哪个数字就好了,那么我们为什么不把他给的串重新定义一下? ///比如他的样例:3 2 1 4 5 我们把他变成 1 2 3 4 5 用一个数组记录一下每个数字变成了什么,相当于离散化了一下3-1;2-2;1-3;4-4;5-5; ///现在我们的第二串1 2 3 4 5 按我们离散化的表示:3 2 1 4 5 ///可能有些人已经懂了,我们把第一个串离散化后的数组是满足上升,反过来,满足上升的也就是满足原串的排列顺序的,(如果你不懂的话可以多看几遍这个例子)O(∩_∩)O~ ///好了 ,现在的问题就变成了求一个最长不下降序列!好了!解决完成! #include<bits/stdc++.h> using namespace std; const int N=100010; int n; int a[N],b[N],belong[N+10],q[N],len=0; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; belong[a[i]]=i; } for(int i=1;i<=n;i++) cin>>b[i]; q[0]=-2e9; for(int i=1;i<=n;i++) { if(belong[b[i]]>q[len]) { q[++len]=belong[b[i]]; continue; } else { int l=0,r=len; while(l<r) { int mid=l+r>>1; if(q[mid]<belong[b[i]]) l=mid+1; else r=mid; } q[l]=belong[b[i]]; } } cout<<len; return 0; } 9-1 区间DP 石子合成问题://https://blog.csdn.net/qq_45069496/article/details/123159802?ops_request_misc=&request_id=&biz_id=102&utm_term=%E7%9F%B3%E5%AD%90%E5%90%88%E5%B9%B6%E9%97%AE%E9%A2%98%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-123159802.142^v86^control,239^v2^insert_chatgpt&spm=1018.2226.3001.4187 区间dp难点在于循环,个人认为 #include<bits/stdc++.h> using namespace std; const int N=1010; int n; int s[N],f[N][N]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++) s[i]+=s[i-1]; for(int len=2;len<=n;len++) { for(int i=1;i+len-1<=n;i++) { int j=i+len-1; for(int k=i;k<j;k++) f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); } } cout<<f[1][n]; return 0; } 9-2 用单调队列实现动态规划,优化效率 #include<bits/stdc++.h> using namespace std; const int N=1000010; int use[2*N],l,r,n,hh,tt,q[N],a[2*N],f[2*N],res=-2e9; int main() { cin>>n>>l>>r; for(int i=1;i<=2*n;i++) f[i]=-1e9; for(int i=l;i<=r;i++) use[i]=1;///标记,当i循环到l,就是第一步 for(int i=0;i<=n;i++) cin>>a[i]; for(int i=0;i<=n;i++) { while(hh<=tt&&q[hh]<i-r+l) hh++;///检查是否出列 while(hh<=tt&&f[i]>f[q[tt]]) tt--;///队尾出列,维护最大值; q[++tt]=i; f[i+l]=f[q[hh]]+a[i+l]; if(use[i]) use[i+l]=1;///标记作用, } for(int i=n+1;i<=n+l;i++) if(use[i]) res=max(res,f[i]); cout<<res; return 0; } /** https://www.luogu.com.cn/problem/CF414B 本题考察思维,状态表示为f[i][j],i是当前的长度,j是最大的数 那么此时本题有两种说法,第一种:f[i][j]+=f[i-1][j|k],即k是j的因数 第二种方法是:f[i+1][j]+=f[i][k|j],即k是j的倍数; 第一种方法可以看出来,我们需要枚举出j所有的因数,时间复杂度较高 而第二种方法,我们则可以通过k+=j来实现 **/ #include<bits/stdc++.h> using namespace std; const int N=2020,mod=1000000007; int n,k,f[N][N]; long long res; int main() { cin>>n>>k; for(int i=1;i<=n;i++) f[1][i]=1; for(int i=1;i<=k-1;i++) for(int j=1;j<=n;j++) for(int k=j;k<=n;k+=j) { f[i+1][k]+=f[i][j];///这样一来每一次,k都是j的倍数, f[i+1][k]%=mod;///每一步都要取模 } for(int i=1;i<=n;i++) { res+=f[k][i]; res%=mod; } cout<<res; return 0; } /** https://www.luogu.com.cn/problem/P1077 芜~,这次状态想对了,状态转移方程也是差不多,但是呢,在处理k的时候出问题了 看了题解才会,唉,任重而道远啊; **/ #include<bits/stdc++.h> using namespace std; const int N=2020,mod=1e6+7; int n,m,f[N][N],a[N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; f[0][0]=1; for(int j=0;j<=m;j++) { for(int i=1;i<=n;i++) { for(int k=0;k<=min(j,a[i]);k++) f[i][j]=(f[i][j]+f[i-1][j-k])%mod; } } cout<<f[n][m]; return 0; } /** https://www.luogu.com.cn/problem/P1115 我无语啦,为啥就是要看题解,好好想想啊喂; 枚举每个从i开始,左到右的最长不上升子序列就OK了; **/ #include<bits/stdc++.h> using namespace std; const int N=500; int n,zuo[N],you[N],res,a[N]; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; zuo[i]=1;///初始化放在哪都可以; you[i]=1; } for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[i]>a[j]) zuo[i]=max(zuo[i],zuo[j]+1); for(int i=n;i>=1;i--) for(int j=i+1;j<=n;j++) if(a[i]>a[j]) you[i]=max(you[i],you[j]+1); for(int i=1;i<=n;i++) res=max(res,zuo[i]+you[i]-1);///我怎么感觉这是贪心(恼) cout<<n-res; return 0; } /** https://www.luogu.com.cn/problem/P1510 气死我啦,又是背包问题,关键是还不会,哎哟,烦死了 估计今天状态不对 **/ #include<bits/stdc++.h> using namespace std; const int N=1000010; int n,m,t,f[N],w[N],v[N]; int main() { cin>>m>>n>>t; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++) for(int j=t;j>=v[i];j--)///必须枚举体力,如果枚举体积的话,要开个sum,从sum开始遍历,会超时 f[j]=max(f[j],f[j-v[i]]+w[i]); if(f[t]<m) cout<<"Impossible"; else { int x=t; while(f[x]>=m) x--;///当第一个体积不大于m时,停止; cout<<t-x-1; } return 0; } /** https://www.luogu.com.cn/problem/P1018 还是经典动态规划,详细思路放代码里 **/ #include<bits/stdc++.h> using namespace std; const int N=2020; int n,k; long long f[N][N],a[N],s[N][N];///a表示存储每一个数字,s表示从i开始,往后数j个位置代表的数(例:123456,s[1][3]=123) ///f表示第i位时,局部最优解是什么,k表示用乘法的次数; int main() { cin>>n>>k; for(int i=1;i<=n;i++) { char c; cin>>c; a[i]=c-'0'; } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) s[i][j]=s[i][j-1]*(long long)10+a[j];///首先表示出s数组,要存longlong for(int i=1;i<=n;i++) f[i][0]=s[1][i];///数组初始化 for(int j=1;j<=k;j++) for(int i=1;i<=n;i++) for(int l=1;l<i;l++) f[i][j]=max(f[i][j],f[l][j-1]*s[l+1][i]);///从l为1开始遍历,寻找局部最优解,再反馈给最优解; cout<<f[n][k]; return 0; } /** 区间DP 狠狠的拷打 **/ ///区间dp1 石子合并:https://www.luogu.com.cn/problem/P1775 ///这个就是典型的区间dp啊,要知道区间dp是枚举的每个区间,区间是1的那就是本身了,就不需要再枚举了 ///如果时至今日还不会的话再去csdn或者acwing上看看课程吧qaq ///不说了我要手打一遍了 #include<bits/stdc++.h> using namespace std; const int N=2020; int n,a[N],s[N],f[N][N];///f[i][j]表示从i到j合并这个区间所用的最小值; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; s[i]=s[i-1]+a[i];///求前缀和,因为我们要枚举每个区间,而每个区间的总和在这里要预处理一下 } for(int len=2;len<=n;len++)///枚举区间,因为len=1的时候就是自己搬自己,不消耗体力,没必要枚举; for(int i=1;i+len-1<=n;i++)///区间左端点是i,区间右端点是i+len-1,所以右端点不能超过n { int l=i,r=i+len-1; f[l][r]=1e9;///因为要求最小值,所以先把这个区间值赋的很大; for(int k=l;k<r;k++) f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);///从l到k的合并区间+从k+1到r的合并区间,再加上合并这俩区间所需要的体力; } /** 这里补充一个方法,可以从len=1开始; memset(f,0x3f,sizeof f); for(int len=2;len<=n;len++) for(int i=1;i+len-1<=n;i++) { int l=i,r=i+len-1; if(len==1) f[l][r]=0;///这里不同; else for(int k=l;k<r;k++) f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]); } **/ cout<<f[1][n];///从1合并到n的最小值 return 0; } ///区间dp2-合并果子加强版:https://www.luogu.com.cn/problem/P1880 ///没什么说法,想象一下,把这个环拆开,然后再补一个从1到n的链,那么我们每次断开的时候是不是就是从断点到区间那个长度? ///经典具体说法请看acwing,我要手打代码了呜呜呜 #include<bits/stdc++.h> using namespace std; const int N=2020; int n,s[N],f[N][N],g[N][N],w[N]; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>w[i]; w[i+n]=w[i]; } for(int i=1;i<=2*n;i++) s[i]=s[i-1]+w[i]; for(int len=2;len<=n;len++) for(int i=1;i+len-1<=2*n;i++) { int l=i,r=i+len-1; f[l][r]=1e9,g[l][r]=-1e9;///注意,赋值一定要赋的足够大; for(int k=l;k<r;k++) { f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]); g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]); } } int res=2e6,ans=-2e6;///记录最小值最大值; for(int i=1;i<=n;i++) { res=min(res,f[i][i+n-1]);///枚举所有区间为n的,找出最小值; ans=max(ans,g[i][i+n-1]); } cout<<res<<endl<<ans; return 0; } /* https://www.luogu.com.cn/problem/P1164 经典的dp问题,我真是服了,一定要多想想这种类型的; 一眼就是背包; 先定义状态,脑袋里不能只有二维状态; f[i]表示有i元时的方案数; 那么就可以转换为经典的背包问题了; */ #include<bits/stdc++.h> using namespace std; const int N=2020; int n,m,w[N],f[N],res; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]; f[0]=1; for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j]+=f[j-w[i]]; cout<<f[m]; return 0; }
标签:std,main,int,namespace,cin,using,动态,规划,dp From: https://www.cnblogs.com/o-Sakurajimamai-o/p/17427991.html