[关闭]
@ysner 2018-10-17T00:31:46.000000Z 字数 1536 阅读 2205

[SHOI2008]循环的债务

DP 背包


题面

戳我

解析

感觉比较套路。
当然前提是注意到各币值互不影响。

那就简单了。
表示到第种币值,元,元。显然钱的总量固定,的钱数可以顺带算出来。
这样起始状态和最终状态都知道。

然后枚举下他们交易完后各剩多少张该币值的钱,直接转移即可。
若设分别表示钱数的变化量,那么实际交换次数为。(这个手算下就出来了)。

如果要过,需要倒着枚举币值。因为币值越大,能有效转移到后面去的状态就越少,很容易被掉。
复杂度应该是,但是很难跑满。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #define re register
  8. #define il inline
  9. using namespace std;
  10. 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};
  11. il int gi()
  12. {
  13. re int x=0,t=1;
  14. re char ch=getchar();
  15. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  16. if(ch=='-') t=-1,ch=getchar();
  17. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  18. return x*t;
  19. }
  20. int main()
  21. {
  22. X1=gi();X2=gi();X3=gi();
  23. for(re int i=1;i<=3;++i)
  24. for(re int j=6;j>=1;--j)
  25. {
  26. num[i][j]=gi();
  27. snum[j]+=num[i][j];
  28. ss[i]+=val[j]*num[i][j];
  29. }
  30. sum=ss[1]+ss[2]+ss[3];
  31. memset(f[0],63,sizeof(f[0]));
  32. f[0][ss[1]][ss[2]]=0;
  33. for(re int i=1;i<=6;++i)
  34. {
  35. re int now=i&1,pre=now^1;
  36. memset(f[now],63,sizeof(f[now]));
  37. for(re int j=0;j<=sum;++j)
  38. for(re int k=0;k<=sum-j;++k)
  39. {
  40. if(f[pre][j][k]>1e9) continue;
  41. for(re int n=0;n<=snum[i];++n)
  42. for(re int m=0;m<=snum[i]-n;++m)
  43. {
  44. re int A=j+(n-num[1][i])*val[i],B=k+(m-num[2][i])*val[i];
  45. if(A>=0&&B>=0&&A<=1000&&sum-A-B>=0)
  46. {
  47. re int w=abs(n-num[1][i])+abs(m-num[2][i])+abs(num[1][i]+num[2][i]-n-m)>>1;
  48. f[now][A][B]=min(f[now][A][B],f[pre][j][k]+w);
  49. }
  50. }
  51. }
  52. }
  53. re int las1=ss[1]-X1+X3,las2=ss[2]+X1-X2;
  54. if(las1<0||las2<0||sum-las1-las2<0||f[0][las1][las2]>1e9) puts("impossible");
  55. else printf("%d\n",f[0][las1][las2]);
  56. return 0;
  57. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注