P4141 消失之物(退背包)

洛谷 p4141消失之物 (退背包)


题目链接

思路分析

如果我们去掉删除一个物品的限制,该题便转化为一个01背包了,但目前有删除物品的

限制,跑\(n\)次01背包?这样肯定会tle。我们考虑先忽略掉删除物品的限制,求出所有

的方案数,然后再枚举删除哪个物品,减去不符合的方案数,便可得到答案,有点类似容

斥。

算法分析

我们设\(f[i][1]\)表示装入物品总体积为\(i\),删除掉目前所枚举物品时的方案数.设

\(f[i][0]\)表示装入物品总体积为\(i\),未删除物品时的方案数。

先预处理无限制时的方案数

f[0][1]=f[0][0]=1;
for(int i=1;i<=n;i++){//i为物品编号
		for(int j=m;j>=v[i];j--){
			f[j][0]+=f[j-v[i]][0];
			f[j][0]%=10;//mod10是因为题目要求输出尾数字
		}
	}

接着减掉不符合目前情况的方案数

for(int i=1;i<=n;i++){//i为当前删除物品
		for(int j=1;j<=m;j++){
			if(j-v[i]>=0){
				f[j][1]=(f[j][0]-f[j-v[i]][1]+10)%10;
			}
			else{
				f[j][1]=(f[j][0]+10)%10;
			}
			cout<<f[j][1];
		}
		cout<<endl;
	}

为什么我们要这样递推

f[j][1]=(f[j][0]-f[j-v[i]][1]+10)%10;

我们来分析,\(f[ j-v[i] ][1]\)为占用体积为\(j-v[i]\)且不包含\(i\)物品的方案数,

为什么要把它减掉呢?因为\(f[ j-v[i] ] [1]\)表示所有使用物品\(i\)体积达到\(j\)的方案数,因为只要加上\(v[i]\)此时使用\(i\)且体积为\(j\)很明显,这是需要减掉的;

这样代码就可以写出来了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
using namespace std;
const int maxn=1e5;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return ret*f;
}
int f[maxn][2];
int v[maxn];
int n;
int m;
int main(){
	n=read();
	m=read();
	for(int i=1;i<=n;i++){
		v[i]=read();
	}
	f[0][1]=f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=m;j>=v[i];j--){
			f[j][0]+=f[j-v[i]][0];
			f[j][0]%=10;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(j-v[i]>=0){
				f[j][1]=(f[j][0]-f[j-v[i]][1]+10)%10;
			}
			else{
				f[j][1]=(f[j][0]+10)%10;
			}
			cout<<f[j][1];
		}
		cout<<endl;
	}
	return 0;
}

完结撒花

posted @ 2020-10-14 10:29  折翼的小鸟先生  阅读(125)  评论(0编辑  收藏  举报