洛谷P1450 [HAOI2008]硬币购物 背包+容斥

无限背包+容斥?


观察数据范围,可重背包无法通过,假设没有数量限制,利用用无限背包

进行预处理,因为实际硬币数有限,考虑减掉多加的部分

如何减?利用容斥原理,减掉不符合第一枚硬币数的,第二枚,依次类推

加上不符第一枚和第二枚的方案,第一枚和第三枚的方案以此类推,不明

白原理可以去看一下容斥原理

较长代码(懒得优化)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define ll long long
using namespace std;
const int maxn=1e5+10;
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 f*ret;
}
int c[5];
ll s;
ll dp[maxn];
int n;
ll ans;
ll d[5];
ll getn(int x){
	return (d[x]+1)*c[x];
} 
int main(){
//	freopen("a.txt","r",stdin);
	for(int i=1;i<=4;i++){
	//	c[i]=read();
	    cin>>c[i];
	}
	   cin>>n;
//	n=read();
//	cout<<n;
	dp[0]=1;
//	cout<<dp[-10]<<endl;
	for(int i=1;i<=4;i++){
		for(int j=c[i];j<=maxn;j++){
			dp[j]+=dp[j-c[i]];
		}
	}
//	cout<<dp[10]<<endl;
	while(n){
		n--;
		for(int i=1;i<=4;i++){
			//d[i]=read();
			cin>>d[i];
		}
	//	s=read();
	    cin>>s;
		ans=dp[s];
		for(int i=1;i<=4;i++){
			if(s>=getn(i)){
				ans-=dp[s-getn(i)];	
			}
		}
		if(s>=getn(1)+getn(2)){
			ans+=dp[s-getn(1)-getn(2)];
		}
		if(s>=getn(2)+getn(3)){
			ans+=dp[s-getn(2)-getn(3)];
		}
		if(s>=getn(3)+getn(4)){
			ans+=dp[s-getn(3)-getn(4)];
		}
		if(s>=getn(1)+getn(4)){
			ans+=dp[s-getn(1)-getn(4)];
		}
		if(s>=getn(2)+getn(4)){
			ans+=dp[s-getn(4)-getn(2)];
		}
		if(s>=getn(1)+getn(3)){
			ans+=dp[s-getn(1)-getn(3)];
		}
		if(s>=getn(1)+getn(2)+getn(3)){
			ans-=dp[s-getn(1)-getn(2)-getn(3)];
		}
		if(s>=getn(1)+getn(2)+getn(4)){
			ans-=dp[s-getn(1)-getn(2)-getn(4)];
		}
		if(s>=getn(1)+getn(3)+getn(4)){
			ans-=dp[s-getn(1)-getn(3)-getn(4)];
		}
		if(s>=getn(4)+getn(2)+getn(3)){
			ans-=dp[s-getn(4)-getn(2)-getn(3)];
		}
		if(s>=getn(1)+getn(2)+getn(3)+getn(4)){
			ans+=dp[s-getn(1)-getn(2)-getn(3)-getn(4)];
		}
		cout<<ans<<endl;
	}		
	return 0;
} 

完结撒花

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