洛谷 P1450 解題報告
阿新 • • 發佈:2018-05-07
scanf 輸出 條件 -- 付款方法 如果 math 格式 個數
為裝無限個\(i\)時湊成\(s\)的方案數
P1450.硬幣購物
題目描述
硬幣購物一共有\(4\)種硬幣。面值分別為\(c1,c2,c3,c4\)。某人去商店買東西,去了\(tot\)次。每次帶\(d_i\)枚\(c_i\)硬幣,買\(s_i\)的價值的東西。請問每次有多少種付款方法。
輸入輸出格式
輸入格式:
第一行 \(c_1,c_2,c_3,c_4,tot\) 下面\(tot\)行 \(d_1,d_2,d_3,d_4,s\)
輸出格式:
每次的方法數
說明
\(di,s<=100000\)
\(tot<=1000\)
很容易想到的,轉化成多重背包
dp[0]=1;
for(int i=1;i<=4 ;i++)
for(int k=s;k>=0;k--)
if(dp[k])
for(int j=1;j<=a[i];j++)
dp[k+j*c[i]]+=dp[k];
printf("%d\n",dp[s]);
結果當然是\(t\)飛啦
如果我們當成完全背包來做的話,當放入物品個數大於限制條件時的一部分是非法的。
假設僅僅針對價值為\(c\)的物品\(i\),存在數量上限\(d\),要湊成的錢數為\(s\).
\(dp[s]\)
\(dp[s-c*(d+1)]\)為裝無限個\(i\)時湊成\(s-c*(d+1)\)的方案數
相減得到什麽? 不僅是裝上限為\(d\)個時的方案數嗎
然而這只是針對一個物品而言,如果有多個物品呢?
存在多個約束相交的情況,那麽根據容斥原理,多的減,少的回加即可。
code:
#include <cstdio>
#define ll long long
const int N=100010;
int c[5],tot,a[5],s;
ll dp[N];
int main()
{
for(int i=1;i<=4;i++) scanf(" %d",c+i);
scanf("%d",&tot);
dp[0]=1;
for(int i=1;i<=4;i++)
for(int j=c[i];j<=N;j++)
dp[j]+=dp[j-c[i]];
while(tot--)
{
for(int i=1;i<=4;i++) scanf("%d",a+i);
scanf("%d",&s);
ll ans=0;
for(int i=0;i<=15;i++)
{
int flag=0,t=0;
for(int j=1;j<=4;j++)
if((i>>j-1)&1)
{
t+=c[j]*(a[j]+1);
flag^=1;
}
ll tt=(s>=t?dp[s-t]:0);
ans+=(flag?-tt:tt);
}
printf("%lld\n",ans);
}
return 0;
}
註意這時候枚舉子集的方法。
2018.5.4
洛谷 P1450 解題報告