[bzoj2287]消失之物 题解(背包dp)
2287: 【POJ Challenge】消失之物
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1138 Solved: 654
[Submit][Status][Discuss]
Description
ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢?” -- 这是经典的问题了。她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。
Input
第1行:两个整数 N (1 ≤ N ≤ 2 × 103) 和 M (1 ≤ M ≤ 2 × 103),物品的数量和最大的容积。
第2行: N 个整数 W1, W2, ..., WN, 物品的体积。
Output
一个 N × M 的矩阵, Count(i, x)的末位数字。
Sample Input
3 2
1 1 2
1 1 2
Sample Output
11
11
21
11
21
HINT
如果物品3丢失的话,只有一种方法装满容量是2的背包,即选择物品1和物品2。
好久没有做水题了。
首先考虑暴力怎么做:跑n遍背包,如果轮到某个物品丢失就在外层循环到它的时候直接continue(可得90分的好成绩)
#include<cstdio> #include<iostream> #include<cstring> #define re register using namespace std; const int N=2005; int n,m,dp[N][N],w[N]; inline void mod(int &x) { if(x>10000000)x-=10000000; } int main() { scanf("%d%d",&n,&m); for(re int i=1;i<=n;i++) scanf("%d",&w[i]); for(re int now=1;now<=n;now++) { dp[now][0]=1; for(re int i=1;i<=n;i++) { if(i==now)continue; for(int j=m;j>=w[i];j--) mod(dp[now][j]+=dp[now][j-w[i]]); } } for(re int i=1;i<=n;i++) { for(re int j=1;j<=m;j++) printf("%d",dp[i][j]%10); printf("\n"); } return 0; }
之所以会T是因为它算了很多次相同的部分,那么考虑如果只跑一次背包,之后对于每个物品丢失时怎么得到答案
很简单,减去丢失物品的贡献就好了。
#include<cstdio> #include<iostream> #include<cstring> #define re register using namespace std; typedef long long ll; const int N=2005; int n,m,w[N]; int dp[N],ans[N]; int main() { scanf("%d%d",&n,&m); for(re int i=1;i<=n;i++) scanf("%d",&w[i]); dp[0]=1; for(int i=1;i<=n;i++) for(int j=m;j>=w[i];j--) (dp[j]+=dp[j-w[i]])%=10; for(int i=1;i<=n;i++) { memcpy(ans,dp,sizeof(dp)); for(int j=w[i];j<=m;j++) ans[j]=(ans[j]-ans[j-w[i]]+10)%10; for(int j=1;j<=m;j++) printf("%d",(ans[j]+10)%10); puts(" "); } return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。