[SHOI2008]循環的債務
阿新 • • 發佈:2018-10-17
表示 oss 很難 amp har ner com possible fin ,需要倒著枚舉幣值。因為幣值越大,能有效轉移到後面去的狀態就越少,很容易被\(continue\)掉。
復雜度應該是\(O(6*1000^2*30^2=5.4*10^9)\),但是很難跑滿。
https://www.zybuluo.com/ysner/note/1312307
題面
戳我
解析
感覺比較套路。
當然前提是註意到各幣值互不影響。
那就簡單了。
設\(f[i][j][k]\)表示到第\(i\)種幣值,\(A\)有\(i\)元,\(B\)有\(j\)元。顯然錢的總量固定,\(C\)的錢數可以順帶算出來。
這樣起始狀態和最終狀態都知道。
然後枚舉下他們交易完後各剩多少張該幣值的錢,直接轉移即可。
若設\(x,y,z\)分別表示\(A,B,C\)錢數的變化量,那麽實際交換次數為\((\Delta x+\Delta y+\Delta z)\div 2\)。(這個手算下就出來了)。
如果要過\(Bzoj\)
復雜度應該是\(O(6*1000^2*30^2=5.4*10^9)\),但是很難跑滿。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define re register #define il inline using namespace std; int X1,X2,X3,f[2][1005][1005],num[5][10],snum[10],ss[5],sum,val[10]={0,1,5,10,20,50,100}; il int gi() { re int x=0,t=1; re char ch=getchar(); while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar(); if(ch==‘-‘) t=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-48,ch=getchar(); return x*t; } int main() { X1=gi();X2=gi();X3=gi(); for(re int i=1;i<=3;++i) for(re int j=6;j>=1;--j) { num[i][j]=gi(); snum[j]+=num[i][j]; ss[i]+=val[j]*num[i][j]; } sum=ss[1]+ss[2]+ss[3]; memset(f[0],63,sizeof(f[0])); f[0][ss[1]][ss[2]]=0; for(re int i=1;i<=6;++i) { re int now=i&1,pre=now^1; memset(f[now],63,sizeof(f[now])); for(re int j=0;j<=sum;++j) for(re int k=0;k<=sum-j;++k) { if(f[pre][j][k]>1e9) continue; for(re int n=0;n<=snum[i];++n) for(re int m=0;m<=snum[i]-n;++m) { re int A=j+(n-num[1][i])*val[i],B=k+(m-num[2][i])*val[i]; if(A>=0&&B>=0&&A<=1000&&sum-A-B>=0) { re int w=abs(n-num[1][i])+abs(m-num[2][i])+abs(num[1][i]+num[2][i]-n-m)>>1; f[now][A][B]=min(f[now][A][B],f[pre][j][k]+w); } } } } re int las1=ss[1]-X1+X3,las2=ss[2]+X1-X2; if(las1<0||las2<0||sum-las1-las2<0||f[0][las1][las2]>1e9) puts("impossible"); else printf("%d\n",f[0][las1][las2]); return 0; }
[SHOI2008]循環的債務