AcWing 1047. 糖果
考察:01背包dp
错误思路:
很容易想到f[i][j]代表最大值,i代表从前i个物品选,j代表此方案数量和的余数.
像之前的01dp模板题一样,先给f[i][j]赋值f[i-1][j],再是当j>=sum[i]%k时再赋最大值.
错误原因已在注释标明
1 for(int i=1;i<=n;i++) 2 for(int j=0;j<k;j++) 3 { 4 f[i][j] = f[i-1][j];//如果加if判断,当f[i][0]的值只能由f[i-1][0]传递下来 5 if(j>=sum[i]%k) f[i][j] = max(f[i-1][j],f[i-1][(j-sum[i]%k+k)%k]+sum[i]); 6 //也就是说,f[i][0]的方案可以从减去sum[i]后的余数推来,如果加if判断就切断了这条路 7 }
如果+if判断,f[i][0]的值永远只能由余数为0的物品或者f[0][0]推来,但f[i][0]是可能由去掉第i个物品的sum[i]的和推来,加上sum[i]后余数正好为0
正确思路:
去掉if判断,直接取最大值即可.但是注意,有些情况是不可能的,我们需要在初始状态将不可能的情况设置为最小
这个加上sum[i]使得后面体积只要比sum[i]大就会max到更大值
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const int N = 110; 6 typedef long long ll; 7 int sum[N],f[N][N]; 8 int main() 9 { 10 int n,k; 11 scanf("%d%d",&n,&k); 12 memset(f,-0x3f,sizeof f); 13 f[0][0] = 0; 14 for(int i=1;i<=n;i++) scanf("%d",&sum[i]); 15 for(int i=1;i<=n;i++) 16 for(int j=0;j<k;j++) 17 { 18 f[i][j] = max(f[i-1][j],f[i-1][(j-sum[i]%k+k)%k]+sum[i]); 19 } 20 printf("%d\n",f[n][0]); 21 return 0; 22 }
总结:
- sum[i]一定要先%k,因为值有可能过大,我们需要保证j-sum[i]+k的值为正数
- 不能想当然套模板,一定要想想此情况能不能由其他情况推来
2021.3.15 二刷AC,注意下<a[i]的余数的由来即可