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;
}

完结撒花

原文地址:https://www.cnblogs.com/rpup/p/13813390.html