1. 程式人生 > >洛谷P1450 [HAOI2008]硬幣購物 動態規劃 + 容斥原理

洛谷P1450 [HAOI2008]硬幣購物 動態規劃 + 容斥原理

string -1 line sum mes 開始 clas 完全背包 預處理

洛谷P1450 [HAOI2008]硬幣購物

動態規劃 + 容斥原理

1、首先我們去掉限制 假設 能夠取 無數次 也就是說一開始把他當做完全背包來考慮
離線DP 預處理 復雜度 4*v

用f[ i ] 表示 空間 為 i 的方案數
答案ans 其實就是所有方案 - 所有超過限制的方案 限制指的就是題目中限制 某個硬幣有幾枚

然後所有超過限制的方案用容斥來做

所有超過限制的方案 要減 == -1 超過限制的方案 - 2 超過限制的方案 - 3 超過限制的方案 - 4 超過限制的方案
+ 1和2 超過限制的方案 +1和3超過限制的方案 + 1和4 超過限制的方案 ..... - 1和2和3超過限制的方案 .....
+ 1和2和3和4 超過限制的方案
然後這樣容斥就行了 因為只有四個數 相當於只要運算 16 次 就行 計算一次的復雜度為 O(1)

總的時間復雜度 4*maxv + T*(1) 復雜度 O(v+T)

----------------------------------


另外 來自 hzwer 的題解


我想起了cf的某道題。。。
dp預處理+容斥原理
byvoid:
設F[i]為不考慮每種硬幣的數量限制的情況下,得到面值i的方案數。則狀態轉移方程為

F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}

為避免方案重復,要以k為階段遞推,邊界條件為F[0]=1,這樣預處理的時間復雜度就是O(S)。

接下來對於每次詢問,奇妙的解法如下:根據容斥原理,答案為 得到面值S的超過限制的方案數 – 第1種硬幣超過限制的方案數 – 第2種硬幣超過限制的方案數 – 第3種硬幣超過限制的方案數 – 第4種硬幣超過限制的方案數 + 第1,2種硬幣同時超過限制的方案數 + 第1,3種硬幣同時超過限制的方案數 + …… + 第1,2,3,4種硬幣全部同時超過限制的方案數。

當第1種硬幣超過限制時,只要要用到D[1]+1枚硬幣,剩余的硬幣可以任意分配,所以方案數為 F[ S – (D[1]+1)C[1] ],當且僅當(S – (D[1]+1)C[1])>=0,否則方案數為0。其余情況類似,每次詢問只用問16次,所以詢問的時間復雜度為O(1)。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <string>
 6 #include <algorithm>
 7
#include <iomanip> 8 #include <iostream> 9 #define ll long long 10 using namespace std ; 11 12 int T,v ; 13 int c[5],d[5]; 14 ll ans ; 15 ll f[100011] ; 16 17 inline int read() 18 { 19 char ch = getchar() ; 20 int x = 0 , f = 1 ; 21 while(ch<0||ch>9) { if(ch==-) f = -1 ; ch = getchar() ; } 22 while(ch>=0&&ch<=9) { x = x*10+ch-48 ; ch = getchar() ; } 23 return x*f ; 24 } 25 26 inline void dfs(int x,int k,ll sum) 27 { 28 if(sum < 0 ) return ; 29 if(x==5) 30 { 31 if(k&1) 32 ans-=f[sum] ; 33 else 34 ans+=f[sum] ; 35 return ; 36 } 37 dfs(x+1,k+1, sum-(d[x]+1)*c[x] ) ; // 確保其 一定超出限制 保證 x 一定 超出 限制 38 dfs(x+1,k,sum) ; 39 } 40 41 int main() 42 { 43 for(int i=1;i<=4;i++) c[ i ] = read() ; 44 T = read() ; 45 f[ 0 ] = 1 ; 46 for(int i=1;i<=4;i++) 47 for(int j=c[ i ];j<=100000;j++) // 動歸時 不要忘記邊界 48 f[ j ]+=f[ j-c[ i ] ] ; 49 while(T--) 50 { 51 for(int i=1;i<=4;i++) d[ i ] = read() ; 52 v = read() ; 53 ans = 0 ; 54 dfs( 1,0,v ) ; 55 printf("%lld\n",ans) ; 56 } 57 return 0 ; 58 }

洛谷P1450 [HAOI2008]硬幣購物 動態規劃 + 容斥原理