zoukankan      html  css  js  c++  java
  • [BZOJ1021][SHOI2008]Debt 循环的债务

    1021: [SHOI2008]Debt 循环的债务

    Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 1092  Solved: 558 [Submit][Status][Discuss]

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

    设f[i][j][k]表示前i个的价值,第一个人有j元,第二个人有k元的最小方案数,转移时枚举当前面值个数即可。

    注意,面值从1-100枚举更快。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<algorithm>
     7 using namespace std;
     8 int p[10]={0,1,5,10,20,50,100};
     9 int sum[10],cnt[10];
    10 int c[10][10];
    11 int x1,x2,x3;
    12 int f[10][1005][1005];
    13 int tot;
    14 int calc(int a,int b,int i) {
    15     return (abs(a-c[1][i])+abs(b-c[2][i])+abs(cnt[i]-a-b-c[3][i]))/2;
    16 }
    17 int main() {
    18     scanf("%d%d%d",&x1,&x2,&x3);
    19     for(int i=1;i<=3;i++) {
    20         for(int j=6;j>=1;j--) {
    21             scanf("%d",&c[i][j]);
    22             cnt[j]+=c[i][j];
    23             sum[i]+=c[i][j]*p[j];
    24         }
    25         tot+=sum[i];
    26     }
    27     memset(f,-1,sizeof(f));
    28     f[0][sum[1]][sum[2]]=0;
    29     for(int i=0;i<6;i++) {
    30         for(int j=0;j<=tot;j++) {
    31             for(int k=0;j+k<=tot;k++) {
    32                 if(f[i][j][k]>=0) {
    33                     f[i+1][j][k]=f[i+1][j][k]==-1?f[i][j][k]:min(f[i][j][k],f[i+1][j][k]);
    34                     for(int a=0;a<=cnt[i+1];a++) {
    35                         for(int b=0;a+b<=cnt[i+1];b++) {
    36                             int suma=j+(a-c[1][i+1])*p[i+1],sumb=k+(b-c[2][i+1])*p[i+1];
    37                             if(suma>=0&&sumb>=0&&suma+sumb<=tot)
    38                                 f[i+1][suma][sumb]=f[i+1][suma][sumb]==-1?f[i][j][k]+calc(a,b,i+1):min(f[i+1][suma][sumb],f[i][j][k]+calc(a,b,i+1));
    39                         }
    40                     }
    41                 }
    42             }
    43         }
    44     }
    45     int a=sum[1],b=sum[2],c=sum[3];
    46     a+=x3-x1,b+=x1-x2,c+=x2-x3;
    47     if(a<0||b<0||c<0||a+b+c!=tot||f[6][a][b]<0) printf("impossible
    ");
    48     else printf("%d
    ",f[6][a][b]);
    49 }
    View Code
    O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~
  • 相关阅读:
    关于求LCA三种方法
    逆序对与本质不同的逆序对
    缩点+割点(tarjan)
    关于线段树
    引爆点
    0 基础认知产品经理
    一款 App 开发到上架
    坚持+时间管理
    测试流程
    Java学习笔记--字符串String、StringBuffer和StringBuilder
  • 原文地址:https://www.cnblogs.com/wls001/p/7666116.html
Copyright © 2011-2022 走看看