您的位置:首页 > 其它

bzoj1042[HAOI2008]硬币购物

2016-08-23 19:29 253 查看
题意就不说了,原处就说的很明白。可以明显的看出是背包问题,但多重背包问题还是有一定难度的,容斥原理与背包的应用还是第一次见,一开始并没有想到(感谢hzw)。

解法是这样的:先求出不限制硬币多少的答案数,通过容斥定理,要减去第一种硬币超过数、第二种硬币超过数、第三种硬币超过数、第四种硬币超过数,加上第一、二种硬币同时超过数、加上第一、三种硬币同时超过数、加上第一、四种硬币同时超过数、加上第二、三种硬币同时超过数、加上第二、四种硬币同时超过数、加上第三、四种硬币同时超过数………………由此类推,就将超过的都减掉了,剩下的就是答案!

恶心但好理解的for

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL c[5],d[5],f[110000];
int main()
{
for(int i=1;i<=4;i++)scanf("%d",&c[i]);
memset(f,0,sizeof(f));f[0]=1;
for(int i=1;i<=4;i++)
for(int j=c[i];j<=100000;j++)
f[j]+=f[j-c[i]];
int n,s;
scanf("%d",&n);
while(n--)
{
for(int i=1;i<=4;i++)scanf("%d",&d[i]);
scanf("%d",&s);LL ans=f[s];
for(int i=1;
4000
i<=4;i++)
if(s>=(d[i]+1)*c[i])ans-=f[s-(d[i]+1)*c[i]];
for(int i=1;i<=3;i++)
for(int j=i+1;j<=4;j++)
if(s>=((d[i]+1)*c[i]+(d[j]+1)*c[j]))
ans+=f[s-((d[i]+1)*c[i]+(d[j]+1)*c[j])];
for(int i=1;i<=2;i++)
for(int j=i+1;j<=3;j++)
for(int k=j+1;k<=4;k++)
if(s>=((d[i]+1)*c[i]+(d[j]+1)*c[j]+(d[k]+1)*c[k]))
ans-=f[s-((d[i]+1)*c[i]+(d[j]+1)*c[j]+(d[k]+1)*c[k])];
if(s>=((d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[3]+1)*c[3]+(d[4]+1)*c[4]))
ans+=f[s-((d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[3]+1)*c[3]+(d[4]+1)*c[4])];
printf("%lld\n",ans);
}
return 0;
}


好看的dfs

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL c[5],d[5],f[110000],ans;
void dfs(int x,int k,int sum)
{
if(sum<0)return ;
if(x==5)
{
if(k%2==1)ans-=f[sum];
else ans+=f[sum];
return;
}
dfs(x+1,k+1,sum-(d[x]+1)*c[x]);
dfs(x+1,k,sum);
}
int main()
{
for(int i=1;i<=4;i++)scanf("%d",&c[i]);
memset(f,0,sizeof(f));f[0]=1;
for(int i=1;i<=4;i++)
for(int j=c[i];j<=100000;j++)
f[j]+=f[j-c[i]];
int n,s;
scanf("%d",&n);
while(n--)
{
for(int i=1;i<=4;i++)scanf("%d",&d[i]);
scanf("%d",&s);
ans=0;dfs(1,0,s);
printf("%lld\n",ans);
}
return 0;
}


By_yzh
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: