1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x52揹包DP POJ1742Coins

《演算法競賽進階指南》0x52揹包DP POJ1742Coins

題目連結:http://poj.org/problem?id=1742

多重揹包優化,給出若干面值硬幣,和他們的數量,現在問1-m之間有多少數量是可以通過他們拼出的,複雜度需要控制,而且常數不能大。

解法非常巧妙,用g陣列存上一個1離當前位置的距離,實際上這個問題是一個滑動視窗的問題,對於第i個面值,視窗的長度是數量+1,分別覆蓋了j,j-v,j-2*v...j-s*v。如果在這個視窗之中有一個1,那麼f[j]就是1,否則f[j]就是0,對第i個數之前可拼接的數進行操作,判斷距離是否合適即可判斷是否能拼出當前的數。

思路非常具有創新性,樸素的演算法和二進位制優化以及佇列優化都是沒法過的。

程式碼:

#include<iostream>
#include
<cstdio> #include<cstring> using namespace std; const int maxn = 110,maxm = 100010; int f[maxm],g[maxm]; int a[maxn],c[maxn]; int n,m; int main(){ while(scanf("%d%d",&n,&m),n||m){ for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&c[i]); memset(f,
0,sizeof f); f[0]=1; for(int i=1;i<=n;i++) { for(int j=0;j<=m;j++)g[j]=0; for(int j=a[i];j<=m;j++){ if(!f[j] && f[j-a[i]] && g[j-a[i]]<c[i]){ f[j]=1; g[j]=g[j-a[i]]+1; } } }
int ans=0; for(int i=1;i<=m;i++)ans+=f[i]; cout<<ans<<endl; } }