P4141 消失之物
P4141 消失之物
◦ ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN。 由于她的疏忽, 第 i 个物
品丢失了。 “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法
呢?” -- 这是经典的问题了。
◦她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i,
x) 表格。
◦ N,M<=3000
题解
◦设f[x]表示全部物品都可用恰好装满x体积时的方案数,可以用01背包算法
求出。这是总方案数。
◦然后考虑不选某物品的情况。
◦设g[x]为不选当前物品恰好装满x体积时的方案数。
f[i]加进去当前物品, g[i]不加
f[i]=f[i]+f[i-w[i]]
f[i]=g[i]+g[i-w[i]]
g[i]=f[i]-g[i-w[i]]
◦当x小于w[i]时, i物品一定不会被选上 g[i]=f[i]。
◦当x大于等于w[i]时, i物品可能会被选上,直接求不选的情况比较困难。
◦总方案数及f[x],不选的方案数可以想为先不选i再最后把i选上,即g[x-w[i]]。
◦所以g[x]=f[x]-g[x-w[i]], x从小到大枚举计算g即可。
◦每次都是线性复杂度,一共n次计算,总复杂度是O(n*m)
另两种理解方式
◦ 1:也可以用生成函数来理解。
◦ 2:直接考虑程序代码也可以很简单的理解。
上面有些晦涩难懂,下面看一个仍然晦涩难懂的描述
设 f[ j ][ 0 ] 为体积为 j ,第 i 个物品可以选上的时候,体积为 j 的最大方案数
设 f[ j ][ 1 ] 为体积为 j ,第 i 个物品没选的时候,体积为 j 的最大方案数
假设没有物品消失,那么这就相当于一个01背包问题,f[ x ][ 0 ] 之间的转移
如果有物品消失的话,就相当于在原来的方案数上减去它
对于物品 i ,如果当前的体积可以放进去,那么它就由 选中的方案 - 没选中的方案,因为之前可能选上了 i (QAQ太艰难了)
对于物品 i ,如果当前的体积不能放进去,那么直接由 f[ x ][ 0 ] 转移过来,因为在原来的方案里一定是没有选中它的
代码
#include<bits/stdc++.h> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int maxn=2005; int n,m; int w[maxn]; int f[maxn][2]; int main() { n=read();m=read(); for(int i=1;i<=n;i++) w[i]=read(); f[0][0]=f[0][1]=1;
for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) f[j][0]+=f[j-w[i]][0],f[j][0]%=10;
for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(j>=w[i]) f[j][1]=(f[j][0]-f[j-w[i]][1]+10)%10; else f[j][1]=f[j][0]; f[j][1]=f[j][1]%10; printf("%d",f[j][1]); } printf("\n"); } return 0; }