题目地址:https://www.luogu.org/problemnew/show/P4141
分析:这题当然可以直接暴力枚举去掉哪一个物品,然后每次暴力跑一遍背包,时间复杂度为O(m*n^2),显然超时。由于算去掉哪一个物品比较复杂,我们可以考虑容斥,算出他的补集,也就是选这个物品的方案数,然后用全集减去他的补集得到答案。算全集的过程就是跑一遍01背包,时间复杂度O(n^2),然后枚举去掉的物品i,再枚举背包的容积就j,算选择这个物品凑出这个容积的方案数就相当于算凑出j-w[i]的方案数,然后再强制选择一个i物品,用前面第一遍背包预处理求出的答案减去这个就是最终答案。然而我们还需要考虑一种情况,就是当前枚举的容积小于i物品的体积,也就是说在凑出j体积的背包时一直都没有选择i物品,也就不能去掉它,答案就是前面01背包预处理的值。
代码:
#include<bits/stdc++.h> using namespace std; const int M=2e3+10; int n,m,w[M],f[M],g[M]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&w[i]); f[0]=1; //01背包: for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j]=(f[j]+f[j-w[i]])%10; for(int i=1;i<=n;i++) { memset(g,0,sizeof(g)); g[0]=1; for(int j=1;j<=m;j++) { if(j>=w[i])g[j]=(f[j]-g[j-w[i]]+10)%10; else g[j]=f[j]; printf("%d",g[j]); } puts(""); } return 0; }