bzoj 1021[SHOI2008]Debt 循环的债务 - dp
1021: [SHOI2008]Debt 循环的债务
Time Limit: 1 Sec Memory Limit: 162 MBDescription
Alice、Bob和Cynthia总是为他们之间混乱的债务而烦恼,终于有一天,他们决定坐下来一起解决这个问题。
不过,鉴别钞票的真伪是一件很麻烦的事情,于是他们决定要在清还债务的时候尽可能少的交换现金。比如说,Al
ice欠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|。
初看这题,会感觉十分混乱,因为感觉每个人的状态都是不确定的
但是忽然我们会发现一个结论
A 给 B k 元 , 而 B 又给 C k元的情况是不存在的。
因为A 可以越过B直接给C, 这样次数更少
所以其实对于每种价值,只对应了6种情况:
A -> BC
B -> AC
C -> AB
AB -> C
AC -> B
BC -> A
设 f[i][j][k] 表示处理完钱i种价值, A有 j 元, B 有 k 元的最小次数
可以直接枚举上面6种情况进行转移
还有一个不错的优化:
当我们进行到第i种价值时,ABC之间的钱数只会变化后面价值gcd的倍数,我们不用全部枚举了。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define LL long long 6 7 using namespace std; 8 9 int f[2][1100][1100]; 10 int s[4], e[4], x[4]; 11 int num[4][6]; 12 int now = 0; 13 int sum = 0; 14 const int inf = 1061109567; 15 int val[6] = {1, 5, 10, 20, 50, 100}; 16 17 int gcd(int a, int b) 18 { 19 if(b == 0) { 20 return a; 21 } else { 22 return gcd(b, a % b); 23 } 24 } 25 inline LL read() 26 { 27 LL x = 0, w = 1; char ch = 0; 28 while(ch < '0' || ch > '9') { 29 if(ch == '-') { 30 w = -1; 31 } 32 ch = getchar(); 33 } 34 while(ch >= '0' && ch <= '9') { 35 x = x * 10 + ch - '0'; 36 ch = getchar(); 37 } 38 return x * w; 39 } 40 41 42 void dp(int v, int va, int vb) // v 表示当前交换的钱 43 { 44 for(int i = 1; i <= num[1][v]; i++) { // A 给 BC 45 if(va - i * val[v] < 0) { 46 break; 47 } 48 for(int j = 0; j <= i; j++) { 49 if(vb + j * val[v] > 1000 || sum - va - vb + (i - j) * val[v] > 1000) { 50 break; 51 } 52 f[now ^ 1][va - i * val[v]][vb + j * val[v]] = min(f[now][va][vb] + i, f[now ^ 1][va - i * val[v]][vb + j * val[v]]); 53 } 54 } 55 for(int i = 1; i <= num[2][v]; i++) { // B 给 AC 56 if(vb - i * val[v] < 0) { 57 break; 58 } 59 for(int j = 0; j <= i; j++) { 60 if(va + j * val[v] > 1000 || sum - va - vb + (i - j) * val[v] > 1000) { 61 break; 62 } 63 f[now ^ 1][va + j * val[v]][vb - i * val[v]] = min(f[now][va][vb] + i, f[now ^ 1][va + j * val[v]][vb - i * val[v]]); 64 } 65 } 66 for(int i = 1; i <= num[3][v]; i++) { // C 给 AB 67 if(sum - va - vb - i * val[v] < 0) { 68 break; 69 } 70 for(int j = 0; j <= i; j++) { 71 if(vb + j * val[v] > 1000 || va + (i - j) * val[v] > 1000) { 72 break; 73 } 74 f[now ^ 1][va + (i - j) * val[v]][vb + j * val[v]] = min(f[now][va][vb] + i, f[now ^ 1][va + (i - j) * val[v]][vb + j * val[v]]); 75 } 76 } 77 for(int i = 0; i <= num[2][v]; i++) { // BC 给 A 78 if(vb - i * val[v] < 0) { 79 break; 80 } 81 for(int j = 0; j <= num[3][v]; j++) { 82 if(sum - va - vb - j * val[v] < 0 || va + (i + j) * val[v] > 1000) { 83 break; 84 } 85 f[now ^ 1][va + (i + j) * val[v]][vb - i * val[v]] = min(f[now][va][vb] + i + j, f[now ^ 1][va + (i + j) * val[v]][vb - i * val[v]]); 86 } 87 } 88 for(int i = 0; i <= num[1][v]; i++) { // BA 给 C 89 if(va - i * val[v] < 0) { 90 break; 91 } 92 for(int j = 0; j <= num[2][v]; j++) { 93 if(vb - j * val[v] < 0 || sum - va - vb + (i + j) * val[v] > 1000) { 94 break; 95 } 96 f[now ^ 1][va - i * val[v]][vb - j * val[v]] = min(f[now][va][vb] + i + j, f[now ^ 1][va - i * val[v]][vb - j * val[v]]); 97 } 98 } 99 for(int i = 0; i <= num[1][v]; i++) { // AC 给 B 100 if(va - i * val[v] < 0) { 101 break; 102 } 103 for(int j = 0; j <= num[3][v]; j++) { 104 if(sum - va - vb - j * val[v] < 0 || vb + (i + j) * val[v] > 1000) { 105 break; 106 } 107 f[now ^ 1][va - i * val[v]][vb + (i + j) * val[v]] = min(f[now][va][vb] + i + j, f[now ^ 1][va - i * val[v]][vb + (i + j) * val[v]]); 108 } 109 } 110 } 111 int main() 112 { 113 for(int i = 1; i <= 3; i++) { 114 x[i] = read(); 115 } 116 for(int i = 1; i <= 3; i++) { 117 for(int j = 5; j >= 0; j--) { 118 num[i][j] = read(); 119 s[i] += num[i][j] * val[j]; 120 } 121 sum += s[i]; 122 } 123 e[1] = s[1] - x[1] + x[3]; 124 e[2] = s[2] - x[2] + x[1]; 125 e[3] = s[3] - x[3] + x[2]; 126 if(e[1] < 0 || e[2] < 0 || e[3] < 0) { 127 printf("impossible\n"); 128 return 0; 129 } 130 memset(f, 0x3f, sizeof f); 131 f[0][s[1]][s[2]] = 0; 132 for(int i = 0; i < 6; i++) { 133 for(int j = 0; j <= 1000; j++) { 134 for(int k = 0; k <= 1000; k++) { 135 f[now ^ 1][j][k] = f[now][j][k]; 136 } 137 } 138 int gg = val[i]; 139 for(int j = i + 1; j < 6; j++) { 140 gg = gcd(val[j], gg); 141 } 142 int x = 0, y = 0; 143 while((e[1] - x) % gg != 0) { 144 x++; 145 } 146 while((e[2] - y) % gg != 0) { 147 y++; 148 } 149 if((e[3] - (sum - x - y)) % gg != 0) { 150 continue; 151 } 152 for(int j = x; j <= 1000; j += gg) { 153 for(int k = y; k <= 1000; k += gg) { 154 if(sum - j - k < 0) { 155 break; 156 } 157 if(f[now][j][k] == inf) { 158 continue; 159 } 160 dp(i, j, k); 161 } 162 } 163 now = now ^ 1; 164 } 165 if(f[now][e[1]][e[2]] == inf) { 166 printf("impossible\n"); 167 } else { 168 printf("%d\n", f[now][e[1]][e[2]]); 169 } 170 return 0; 171 } 172 173 /* 174 175 10 0 0 176 177 0 1 0 0 0 0 178 179 0 0 0 3 0 10 180 181 0 0 3 0 0 0 182 183 */