1. 程式人生 > >洛谷 P1450 解題報告

洛谷 P1450 解題報告

scanf 輸出 條件 -- 付款方法 如果 math 格式 個數

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]\)

為裝無限個\(i\)時湊成\(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 解題報告