1. 程式人生 > >BZOJ.1021.[SHOI2008]循環的債務(DP)

BZOJ.1021.[SHOI2008]循環的債務(DP)

continue 狀態 ++ 題目 ssi 根據 ron spa owa

題目鏈接

不同面額的鈔票是可以分開考慮的。
↑其實並不很明白具體(證明?),反正是可以像背包一樣去做。
f[x][i][j]表示用前x種面額鈔票滿足 A有i元 B有j元 (C有sum-i-j)所需交換的最少數量(=(abs(ΔA)+abs(ΔB)+abs(ΔA+ΔB))/2)。
(i,j是在本來就有的鈔票的基礎上的,因為初始得是f[0][sa][sb]=0,這樣轉移後的價格是根據差值變的)
轉移時枚舉i,j,再枚舉最終A有a張x面值鈔票,B有b張x面值鈔票 (據此可以算出要交換的鈔票數)。
復雜度。。看起來很大但是可能因為很多非法狀態,所以跑的不慢。

//28276kb   676ms
#include <cstdio>
#include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() const int val[7]={1,5,10,20,50,100},INF=0x3f3f3f3f; int n,have[3][7],num[7],f[7][1002][1002]; inline int read() { int now=0,f=1;register char c=gc(); for(;!isdigit(c);c=gc()) if(c=='-') f=-1; for
(;isdigit(c);now=now*10+c-'0',c=gc()); return now*f; } int main() { int x1=read(),x2=read(),x3=read(),sa=0,sb=0,sc=0; for(int j=5; ~j; --j) num[j]+=(have[0][j]=read()), sa+=have[0][j]*val[j]; for(int j=5; ~j; --j) num[j]+=(have[1][j]=read()), sb+=have[1][j]*val[j]; for
(int j=5; ~j; --j) num[j]+=(have[2][j]=read()), sc+=have[2][j]*val[j]; int sum=sa+sb+sc; int ea=sa-x1+x3,eb=sb+x1-x2,ec=sum-ea-eb; memset(f,0x3f,sizeof f); f[0][sa][sb]=0; for(int x=0; x<6; ++x) { for(int i=0; i<=sum; ++i) for(int k,j=0; i+j<=sum; ++j) { if(f[x][i][j]>=INF) continue; k=sum-i-j; int nowa,nowb,deltaA,deltaB; for(int a=0; a<=num[x]; ++a) { deltaA=a-have[0][x], nowa=i+deltaA*val[x]; if(nowa<0) continue;//給出太多的x鈔票不行 for(int b=0; a+b<=num[x]; ++b) { deltaB=b-have[1][x], nowb=j+deltaB*val[x]; if(nowb<0 || sum-nowa-nowb<0) continue; f[x+1][nowa][nowb]=std::min(f[x+1][nowa][nowb],f[x][i][j]+((std::abs(deltaA)+std::abs(deltaB)+std::abs(deltaA+deltaB))>>1)); } } } } if(f[6][ea][eb]<INF) printf("%d",f[6][ea][eb]); else puts("impossible"); return 0; }

BZOJ.1021.[SHOI2008]循環的債務(DP)