bzoj 1021[SHOI2008]Debt 循环的债务 - dp

Posted 大财主

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1021[SHOI2008]Debt 循环的债务 - dp相关的知识,希望对你有一定的参考价值。

1021: [SHOI2008]Debt 循环的债务

Time Limit: 1 Sec  Memory Limit: 162 MB

Description

  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 */
View Code

 

































以上是关于bzoj 1021[SHOI2008]Debt 循环的债务 - dp的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj1021] [SHOI2008]Debt 循环的债务

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

BZOJ1021

BZOJ1021 SHOI2008循环的债务

BZOJ1021 [SHOI2008]循环的债务

BZOJ.1021.[SHOI2008]循环的债务(DP)