1. 程式人生 > >poj1742 Coins

poj1742 Coins

題目描述

題解:

這是個多重揹包,但是一般的複雜度是過不去這題的。

所以有二進位制優化和單調佇列優化。

二進位制優化是將數量$n$化為多個數,而且這些數能表示出$1~n$中的任意數。

怎麼保證?

想起二進位制,我們可以將$n$分為$1+2+4+8+……+k$,$k$可以是任意數。

單調佇列怎麼優化?

我們發現,轉移時$f[i]$的狀態可由$f[i-a*k]$轉移而來,於是對於$0~a-1$進行列舉,每次更新與之同餘的所有數。

單調佇列程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using
namespace std; #define N 105 #define M 100050 int n,m,a[N],c[N]; bool f[M]; int sta[M],hd,tl; int main() { while(scanf("%d%d",&n,&m)) { if(!n&&!m)break; 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++) { if(c[i]==1) { for(int j=m;j>=a[i];j--) if(f[j-a[i]])f[j]=1; }else if(a[i]*c[i]>=m) { for(int j=a[i];j<=m;j++)
if(f[j-a[i]])f[j]=1; }else { for(int j=0;j<a[i];j++) { hd=1,tl=0; for(int k=j;k<=m;k+=a[i]) { while(hd<=tl&&sta[hd]+c[i]*a[i]<k)hd++; if(!f[k]) { if(hd<=tl)f[k]=f[sta[hd]]; }else { sta[++tl]=k; } } } } } int ans = 0; for(int i=1;i<=m;i++)ans+=f[i]; printf("%d\n",ans); } return 0; }