[bzoj1021][SHOI2008]Debt 循环的债务 (动态规划)

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|。

 

分析

     唔……好像没什么难的吧,设f(i,j)表示从初始状态到“A手中持有i元,B手中持有j元,C手中持有Sum-i-j元”这一状态需要的最少操作数,然后dp即——

     ——等等……这里转移方向是加减两个方向都可以啊?不可能是dp吧……?

     于是我就在网上看了巨神们的题解……

     啊……看来又是我蠢了……

     他们的思路大概是这样的……考虑这样一个事实:对于同样一种面值的纸币,最优解中不可能出现“A给B,B给C”这样的循环交付,否则我们只要让A直接给C就可以得到更优的解。于是我们就可以从面值来枚举,对每种面值分别dp更新答案。

     代码应该也比较好懂吧,只不过我写得似乎太冗长了……

 

ExpandedBlockStart.gif
 1  /* *************************************************************
 2      Problem: 1021
 3      User: AsmDef
 4      Language: C++
 5      Result: Accepted
 6      Time:488 ms
 7      Memory:8648 kb
 8  *************************************************************** */
 9  
10  /* ********************************************************************* */
11  /* *********************By Asm.Def-Wu Jiaxin**************************** */
12  /* ********************************************************************* */
13 #include <cstdio>
14 #include <cstring>
15 #include <cstdlib>
16 #include <ctime>
17 #include <cctype>
18 #include <algorithm>
19  using  namespace std;
20 template< class T>inline  void getd(T &x){
21      char ch = getchar(); bool neg =  false;
22      while(!isdigit(ch) && ch !=  ' - ')ch = getchar();
23      if(ch ==  ' - ')ch = getchar(), neg =  true;
24     x = ch -  ' 0 ';
25      while(isdigit(ch = getchar()))x = x *  10 -  ' 0 ' + ch;
26      if(neg)x = -x;
27 }
28  /* ********************************************************************* */
29  const  int maxn =  1002, INF =  0x3f3f3f3f, val[ 6] = { 15102050100};
30  
31  int cnt[ 3][ 6], tot[ 6], Cur[ 3], Tar[ 2], Sum, f[ 2][maxn][maxn]; // Tar[]: 目标状态
32   
33 inline  void init(){
34      int a, b, c;
35     getd(a), getd(b), getd(c);
36      for( int i =  0;i <  3;++i) for( int j =  5;j >=  0;--j){
37         getd(cnt[i][j]);
38         Cur[i] += cnt[i][j] * val[j];
39         tot[j] += cnt[i][j];
40     }
41     Sum = Cur[ 0] + Cur[ 1] + Cur[ 2];
42     Tar[ 0] = Cur[ 0] - a + c;
43     Tar[ 1] = Cur[ 1] - b + a;
44  
45      if(Tar[ 0] <  0 || Tar[ 1] <  0 || Sum - Tar[ 0] - Tar[ 1] <  0){
46         printf( " impossible\n ");
47         exit( 0);
48     }
49 }
50  
51  #define UPD(a, b) (a = min(a, b) )
52 #include <cmath>
53  
54 inline  void work(){
55      const  int rang = Sum +  1;
56      int i, j, k, a, b, t, tmp, da, db, cnta, cntb;
57      bool cur, las;
58      for(i =  0;i <= Sum;++i)memset(f[ 1][i],  0x3fsizeof( int) * rang);
59     f[ 1][Cur[ 0]][Cur[ 1]] =  0;
60      for(i =  0;i <  6;++i){ // 枚举面值
61          cur = i &  1, las = cur ^  1;
62          for(j =  0;j <= Sum;++j)memset(f[cur][j],  0x3fsizeof( int) * rang);
63          for(j =  0;j <= Sum;++j){
64             t = Sum - j;
65              for(k =  0;k <= t;++k){ // 枚举A,B两人的当前资产
66                   if(f[las][j][k] == INF) continue;
67                 UPD(f[cur][j][k], f[las][j][k]);
68                  for(a =  0;a <= tot[i];++a){
69                     tmp = tot[i] - a;
70                      for(b =  0;b <= tmp;++b){ // 枚举当前硬币的目标数量
71                          da = a - cnt[ 0][i], db = b - cnt[ 1][i];
72                         cnta = j + da * val[i], cntb = k + db * val[i];
73                          if(cnta <  0 || cntb <  0 || cnta + cntb > Sum) continue;
74                         UPD(f[cur][cnta][cntb], f[las][j][k] + (abs(da) + abs(db) + abs(da + db)) /  2);
75                     }
76                 }
77             }
78         }
79     }
80      if(f[cur][Tar[ 0]][Tar[ 1]] == INF)printf( " impossible\n ");
81      else printf( " %d\n ", f[cur][Tar[ 0]][Tar[ 1]]);
82 }
83  
84  int main(){
85 #ifdef DEBUG
86     freopen( " test.txt "" r ", stdin);
87  #elif not defined ONLINE_JUDGE
88     freopen( " .in "" r ", stdin);
89     freopen( " .out "" w ", stdout);
90  #endif
91     init();
92     work();
93  
94 #ifdef DEBUG
95     printf( " \n%.2lf sec \n ", ( double)clock() / CLOCKS_PER_SEC);
96  #endif
97      return  0;
98 }
动态规划

 

转载于:https://www.cnblogs.com/Asm-Definer/p/4372749.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值