写题解!!!
题目类型
背包DP,多重背包问题
(但是里面也用到了到达型背包的思想)
解题思路
状态设计:dp[k]==1为当使用j个重量为第i个砝码的重量时,可以实现重量为k
使用三重循环实现
- 第一层枚举第i个砝码的重量(i从1到6)
- 第二层枚举用重量为w[i](w数组记录砝码重量)的砝码的个j(j从1到a[i](a数组记录砝码个数))
- 第三层枚举重量k(k从1000到0倒序循环,不能正序!!!本人多次被着个问题困扰,后来发现只是滚动数组优化,要是正序就成完全背包了,一个砝码被使用无数次)
转移方程:if(dp[k]==1 && k+w[i]<=1000) dp[k+w[i]]=1;
另一种思路:
将第二层循环和第三层循环换一下位置
状态设计:dp[j]==1为当使用k个重量为第i个砝码的重量时,可以实现重量为j
转移方程改为if(dp[j]==1 && j+w[i]<=1000) dp[j+w[i]*k]=1;
核心代码:
for(int i=1;i<=6;i++) {
for(int j=1000;j>=0;j--){
for(int k=1;k<=a[i];k++) {
if(dp[j]==1 && j+w[i]<=1000)
dp[j+w[i]*k]=1;
}
}
}
这样就会先枚举重量,在枚举砝码个数,所以需要将dp[k+w[i]]=1;改为dp[j+w[i]*k]=1;
在法1中,砝码是一个个累加上去的,dp[k+w[i]]=1其实是dp[k+w[i]*1]=1;
相当于你先取第一个砝码算出可达到的重量,在成立重量上加上w[i],再取第二个砝码,当你找到一个成立重量时,这些成立重量中,包含了上一轮时找到的新的成立重量,在上面继续累加,直到j等于a[i]
在法2中,个数在递增,每次要在k上加一个w[i]*j;
这样k个重量为w[i]的砝码就会一次性在这些成立重量上累加完,下一次循环就会是新的重量
个人感觉法2更好理解
一些新的想法
如果我让k从0开始循环……
这样做在法2中是成立的,因为这相当于不取第i个砝码,k增加w[i]*j,增加0;
在法1中却不行,因为你没有取第i个砝码,可是j却增加了w[i]……
附上AC代码(法1):
#include<iostream>
using namespace std;
int a[10],dp[1500],w[10]={0,1,2,3,5,10,20};
int ans=0;
int main() {
dp[0]=1;
for(int i=1;i<=6;i++) cin>>a[i];
for(int i=1;i<=6;i++) {
for(int j=1;j<=a[i];j++) {
for(int k=1000;k>=0;k--){
if(dp[k]==1 && k+w[i]<=1000)
dp[k+w[i]]=1;
}
}
}
for(int i=1;i<=1000;i++) {
if(dp[i]==1) ans++;
}
printf("Total=%d",ans);
return 0;
}
注:
- 在给数组赋初始值时,记得把第一个值设为0,否则会wa!!!(不要问我怎么知道的……)
- 最后统计重量个数时,要从1开始循环,因为一个砝码也不取得情况不成立。
- dp[0]记得设为1,设一个砝码都不取的情况已有,否则不管输多大的数进去,也只会输出0……
请各位点个赞支持一下