【BZOJ 1021】 [SHOI2008]Debt 循环的债务

来源:互联网 发布:韩日世界杯假球 知乎 编辑:程序博客网 时间:2024/05/02 01:15

1021: [SHOI2008]Debt 循环的债务

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 605  Solved: 302
[Submit][Status]

Description

Alice、Bob和Cynthia总是为他们之间混乱的债务而烦恼,终于有一天,他们决定坐下来一起解决这个问题。不过,鉴别钞票的真伪是一件很麻烦的事情,于是他们决定要在清还债务的时候尽可能少的交换现金。比如说,Alice欠Bob 10元,而Cynthia和他俩互不相欠。现在假设Alice只有一张50元,Bob有3张10元和10张1元,Cynthia有3张20元。一种比较直接的做法是:Alice将50元交给Bob,而Bob将他身上的钱找给Alice,这样一共就会有14张钞票被交换。但这不是最好的做法,最好的做法是:Alice把50块给Cynthia,Cynthia再把两张20给Alice,另一张20给Bob,而Bob把一张10块给C,此时只有5张钞票被交换过。没过多久他们就发现这是一个很棘手的问题,于是他们找到了精通数学的你为他们解决这个难题。

Input

输入的第一行包括三个整数:x1、x2、x3(-1,000≤x1,x2,x3≤1,000),其中 x1代表Alice欠Bob的钱(如果x1是负数,说明Bob欠了Alice的钱) x2代表Bob欠Cynthia的钱(如果x2是负数,说明Cynthia欠了Bob的钱) x3代表Cynthia欠Alice的钱(如果x3是负数,说明Alice欠了Cynthia的钱)接下来有三行,每行包括6个自然数: a100,a50,a20,a10,a5,a1 b100,b50,b20,b10,b5,b1 c100,c50,c20,c10,c5,c1 a100表示Alice拥有的100元钞票张数,b50表示Bob拥有的50元钞票张数,以此类推。另外,我们保证有a10+a5+a1≤30,b10+b5+b1≤30,c10+c5+c1≤30,而且三人总共拥有的钞票面值总额不会超过1,000。

Output

如果债务可以还清,则输出需要交换钞票的最少张数;如果不能还清,则输出“impossible”(注意单词全部小写,输出到文件时不要加引号)。

Sample Input

输入一
10 0 0
0 1 0 0 0 0
0 0 0 3 0 10
0 0 3 0 0 0
输入二
-10 -10 -10
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

Sample Output

输出一
5
输出二
0

HINT

对于100%的数据,x1、x2、x3 ≤ |1,000|。



dp。


首先要明确一点,这道题只可能有六种转移:

A-->BC 

B-->AC

C-->AB

BC-->A

AC-->B

AB-->C


为什么是这样呢?


比如A欠B10元,B欠C20元,那么直接A-->C10元,B-->C10元即可,也就是AB-->C;没必要A-->B10元,B-->C20元。


也就是说一张钱要直接给需要的人,不要有中转。


接下来就可以分这六种情况来dp了。


f[i][va][vb]表示用前i种钞票,使得A从初始到va,B从初始到vb的最小交换次数。


有一个优化是:


我们的目的是到达目标状态ea,eb,ec,而当前只能使用第i种到第6种钞票,求出他们的gcd=g,那么每次最少增加g,如

果无法通过va,vb每次增加g转移到ea,eb那么就不需要处理了。


#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>#define inf 0x3f3f3f3f#include <algorithm>using namespace std;int f[2][1005][1005],a[10],b[10],c[10],ea,eb,ec,sa,sb,sc,now,n,sum,m[10];void read(int &tmp){tmp=0;int fu=1;char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') fu=-1;for (;ch>='0'&&ch<='9';ch=getchar())tmp=tmp*10+ch-'0';tmp*=fu;}int gcd(int a,int b){return b==0?a:gcd(b,a%b);}void C(int &a,int b){if (a>b) a=b;}void Calc(int va,int vb,int k){int vc=sum-va-vb;//a-->bcfor (int i=1;i<=a[k];i++){if (va<i*m[k]) break;for (int j=0;j<=i;j++)if (vb+j*m[k]<=1000&&vc+(i-j)*m[k]<=1000)C(f[now^1][va-i*m[k]][vb+j*m[k]],f[now][va][vb]+i);}//b-->acfor (int i=1;i<=b[k];i++){if (vb<i*m[k]) break;for (int j=0;j<=i;j++)if (va+j*m[k]<=1000&&vc+(i-j)*m[k]<=1000)C(f[now^1][va+j*m[k]][vb-i*m[k]],f[now][va][vb]+i);}//c-->abfor (int i=1;i<=c[k];i++){if (vc<i*m[k]) break;for (int j=0;j<=i;j++)if (va+j*m[k]<=1000&&vb+(i-j)*m[k]<=1000)C(f[now^1][va+j*m[k]][vb+(i-j)*m[k]],f[now][va][vb]+i);}//bc-->afor (int i=0;i<=b[k];i++){if (va+i*m[k]>1000||vb-i*m[k]<0) break;for (int j=0;j<=c[k];j++)if (va+(i+j)*m[k]<=1000&&vc-j*m[k]>=0)C(f[now^1][va+(i+j)*m[k]][vb-i*m[k]],f[now][va][vb]+i+j);}//ac-->bfor (int i=0;i<=a[k];i++){if (vb+i*m[k]>1000||va-i*m[k]<0) break;for (int j=0;j<=c[k];j++)if (vb+(i+j)*m[k]<=1000&&vc-j*m[k]>=0)C(f[now^1][va-i*m[k]][vb+(i+j)*m[k]],f[now][va][vb]+i+j);}        //ab-->cfor (int i=0;i<=b[k];i++){if (vc+i*m[k]>1000||vb-i*m[k]<0) break;for (int j=0;j<=a[k];j++)if (vc+(i+j)*m[k]<=1000&&va-j*m[k]>=0)C(f[now^1][va-j*m[k]][vb-i*m[k]],f[now][va][vb]+i+j);}}void dp(){now=0;for (int i=0;i<=1000;i++)for (int j=0;j<=1000;j++)f[now][i][j]=inf;f[now][sa][sb]=0;if (ea<0||eb<0||ec<0){printf("impossible\n");return;}for (int i=1;i<=6;i++){for (int j=0;j<=1000;j++)for (int k=0;k<=1000;k++)f[now^1][j][k]=f[now][j][k];int g=m[i],x,y;for (int j=i+1;j<=6;j++)g=gcd(g,m[j]);for (x=0;(ea-x)%g;x++);for (y=0;(eb-y)%g;y++);if ((ec-(sum-x-y))%g) continue;for (int j=x;j<=1000;j+=g){if (sum-j-y<0) break;for (int k=y;k<=1000;k+=g){int vc=sum-j-k;if (vc<0) break;if (f[now][j][k]==inf) continue;Calc(j,k,i);}}now^=1;}if (f[now][ea][eb]==inf) printf("impossible\n");else printf("%d\n",f[now][ea][eb]);}int main(){m[6]=100,m[5]=50,m[4]=20,m[3]=10,m[2]=5,m[1]=1;int x1,x2,x3;        read(x1),read(x2),read(x3);for (int i=6;i;i--)read(a[i]),sa+=a[i]*m[i];for (int i=6;i;i--)read(b[i]),sb+=b[i]*m[i];for (int i=6;i;i--)read(c[i]),sc+=c[i]*m[i];ea=sa-x1+x3,eb=sb+x1-x2,ec=sc-x3+x2;sum=sa+sb+sc;dp();return 0;}



感悟:

1.WA是因为sa,sb,sc求错。。


2.gcd的优化值得借鉴:无论如何都到不了目标状态的情况不必转移

1 0