BZOJ1021 [SHOI2008]循环的债务
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|。
题解
可以发现他们把钱还清等价于三个人最后持有的钱数为他们应该有的钱(即他们本来有的减他们欠别人的加别人欠他们的)。
设他们的目标钱数分别是$Ta,Tb,Tc$,
令$f_{k,i,j}$表示只使用第$k$种至第$5$种(种类为$0..5$)钱币,且现在A持有$i$元,B持有$j$元(C持有$S-i-j$元,其中$S$是总钱数),在此条件下要使他们达到目标状态最少需要交换多少钱币。
边界条件:
$$f_{6,i,j}=\begin{cases}
0 & i = Ta, j = Tb\\
\infty & otherwise
\end{cases}$$
转移时,由于A给B钱B再给C钱没有必要(如果B给C的比A给B的多,转化为A给C,B给C;否则,转化为A给B,A给C),所以只有6种情况,即某两个人都给另一个人,或某一个人给另外两个人(其实还有某个人不参与交换或全都不交换,只需看做给了0个,也可以当成上述六种情况之一)。
从大到小转移时由于好多状态用不到(比如A本来有103元时$f_{1,102,0}$用不到,因为只有整百地取),记忆化搜索即可。
代码中加了一个剪枝:若某人把剩下的钱还完也还不清,直接返回INF。
加了这个剪枝后运行时间少了近一半。(bzoj上1312s->764s)
附代码:
#include <algorithm>
#include <cstdio>
using std::min;
const int N = 1050;
const int INF = 100000000;
int f[6][N][N];
int a[6], b[6], c[6];
int sa[6], sb[6], sc[6], sum;
const int v[6] = {100, 50, 20, 10, 5, 1};
int Ta, Tb, Tc;
int dfs(int k, int aa, int bb) {
if (k == 6) return aa == Ta && bb == Tb ? 0 : INF;
int cc = sum - aa - bb;
if (aa - Ta > sa[k] || bb - Tb > sb[k] || cc - Tc > sc[k])
return INF;
if (~f[k][aa][bb]) return f[k][aa][bb];
int &ans = f[k][aa][bb];
ans = INF;
for (int lac = 0; lac <= a[k]; ++lac)
for (int lbc = 0; lbc <= b[k]; ++lbc)
ans = min(ans, dfs(k + 1, aa - lac * v[k], bb - lbc * v[k]) + lac + lbc);
for (int lab = 0; lab <= a[k]; ++lab)
for (int lcb = 0; lcb <= c[k]; ++lcb)
ans = min(ans, dfs(k + 1, aa - lab * v[k], bb + (lab + lcb) * v[k]) + lab + lcb);
for (int lba = 0; lba <= b[k]; ++lba)
for (int lca = 0; lca <= c[k]; ++lca)
ans = min(ans, dfs(k + 1, aa + (lba + lca) * v[k], bb - lba * v[k]) + lba + lca);
for (int lac = 0; lac <= c[k]; ++lac)
for (int lbc = 0; lbc + lac <= c[k]; ++lbc)
ans = min(ans, dfs(k + 1, aa + lac * v[k], bb + lbc * v[k]) + lac + lbc);
for (int lab = 0; lab <= b[k]; ++lab)
for (int lcb = 0; lcb + lab <= b[k]; ++lcb)
ans = min(ans, dfs(k + 1, aa + lab * v[k], bb - (lab + lcb) * v[k]) + lab + lcb);
for (int lba = 0; lba <= a[k]; ++lba)
for (int lca = 0; lca + lba <= a[k]; ++lca)
ans = min(ans, dfs(k + 1, aa - (lba + lca) * v[k], bb + lba * v[k]) + lba + lca);
return ans;
}
int main() {
int gab, gbc, gca;
scanf("%d%d%d", &gab, &gbc, &gca);
for (int i = 0; i < 6; ++i) scanf("%d", &a[i]);
for (int i = 0; i < 6; ++i) scanf("%d", &b[i]);
for (int i = 0; i < 6; ++i) scanf("%d", &c[i]);
sa[5] = a[5];
for (int i = 4; ~i; --i) sa[i] = sa[i + 1] + a[i] * v[i];
sb[5] = b[5];
for (int i = 4; ~i; --i) sb[i] = sb[i + 1] + b[i] * v[i];
sc[5] = c[5];
for (int i = 4; ~i; --i) sc[i] = sc[i + 1] + c[i] * v[i];
Ta = sa[0] - gab + gca;
Tb = sb[0] - gbc + gab;
Tc = sc[0] - gca + gbc;
if (Ta < 0 || Tb < 0 || Tc < 0) return puts("impossible"), 0;
std::fill(f[0][0], f[6][0], -1);
int ans = dfs(0, sa[0], sb[0]);
if (ans > 1000) return puts("impossible"), 0;
printf("%d\n", ans);
return 0;
}
BZOJ1021 [SHOI2008]循环的债务的更多相关文章
- BZOJ1021 SHOI2008循环的债务
dp模拟即可. d[i][j][k]表示使用前i种面值,1号手里钱为j,2号手里钱为k时最少操作数 使用滚动数组压缩空间 #include <cstdio> #include <cs ...
- 【BZOJ1021】[SHOI2008]循环的债务(动态规划)
[BZOJ1021][SHOI2008]循环的债务(动态规划) 题面 BZOJ 洛谷 题解 感觉以前的题目都好小清新啊,我这种智商丢失的选手完全写不动. 这题看着就像一个\(dp\),并且我们发现每种 ...
- [SHOI2008]循环的债务
Description Alice.Bob和Cynthia总是为他们之间混乱的债务而烦恼,终于有一天,他们决定坐下来一起解决这个问题. 不过,鉴别钞票的真伪是一件很麻烦的事情,于是他们决定要在清还债务 ...
- BZOJ.1021.[SHOI2008]循环的债务(DP)
题目链接 不同面额的钞票是可以分开考虑的. ↑其实并不很明白具体(证明?),反正是可以像背包一样去做. f[x][i][j]表示用前x种面额钞票满足 A有i元 B有j元 (C有sum-i-j)所需交换 ...
- 洛谷 P4026 [SHOI2008]循环的债务
水水的dp 设f[i][a][b]表示交换完前i种面值的钞票,第一个人有a元,第二个人有b元的最小代价 直接转移就行了 需要注意的是算的式子 第1个人\(\Delta A\),第二个人\(\Delta ...
- [luogu4026 SHOI2008]循环的债务 (DP)
传送门 吐槽洛谷难度标签qwq Solution 显然是一道神奇的DP,由于总钱数不变,我们只需要枚举前两个人的钱数就可知第三个人的钱数 DP的时候先枚举只用前k个币种,然后枚举前两个人的钱数,然后枚 ...
- bzoj1021 [SHOI2008]Debt 循环的债务
前天打了一场比赛,让我知道自己Dp有多弱了,伤心了一天,没刷bzoj. 昨天想了一天,虽然知道几何怎么搞,但我还是不敢写,让我知道自己几何有多弱了,伤心了一天,没刷bzoj 1021: [SHOI20 ...
- bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务
http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可 ...
- 【BZOJ】【1021】【SHOI2008】Dept循环的债务
DP 去膜拜题解了>_>玛雅原来是动规…… 让我先理解一下为什么要用动规:这个题根据钱数推方案其实是无从下手的……(线性规划?……事实证明我想多了) 啦-我们先来看个超级简化版的问题:怎么 ...
随机推荐
- Xcode括号自动补全以及二次编译后不显示输入
今天遇到了一个大坑,在使用栈来进行计算表达式的时候,发现输入括号就报错,以及二次编译后不显示. 测试了好久,经过无数次debug后. 二次编译不显示还是没搞明白,不过输入倒是没什么问题,就是不显示出来 ...
- c++之函数形参和实参
c++之函数形参和实参讲解 1.非地址型参数 在c++中实现模块化编程时,我们形成会遇到对自定义的函数模块传入参数的操作,即形参.这里主要讲解一个非地址型的形参. 不多说,先看代码: #include ...
- 【3】JMicro微服务-服务超时,重试,重试间隔
如非授权,禁止用于商业用途,转载请注明出处作者:mynewworldyyl 接下来的内容都基于[2]JMicro微服务-Hello World做Demo 微服务中,超时和重试是一个最基本问题下面Dem ...
- OpenERP中自定义模块卸载失败,Postgres数据库删不掉数据库,OpenERP登录不了一直在加载的问题解决方案。
解决方案也就是删除掉不用的数据库,OE会提示当前有N个Session不让Drop数据库. 对于Postgres 9.1 版本,在pgAdmin中查询以下语句: SELECT pg_terminate_ ...
- C#判断字符串中是否包含一个子字符串是可以直接使用Contains()方法
1. 以前判断一个字符串中是否包含另一个子字符串时,习惯使用 IndexOf(); string str = "ABC@QQ"; if(str.IndexOf("@&qu ...
- [作业] Python入门基础--用户登陆
让用户输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定登陆 #__author:Mifen #date: 2018/11/28 import time #自定义本地用户名和密码 user_nam ...
- FocusBI: 微软商业智能教程目录介绍(原创)
关注微信公众号:FocusBI 查看更多文章:加QQ群:808774277 获取学习资料和一起探讨问题. <商业智能教程>pdf下载地址 链接:https://pan.baidu.com/ ...
- 724_Find-Pivot-Index
目录 724_Find-Pivot-Index Description Solution Java solution Python solution 724_Find-Pivot-Index Desc ...
- [PY3]——函数——生成器(yield关键字)
函数—生成器篇 1. 认识和区分可迭代or生成器 1.1 可迭代对象 当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象 当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代 ...
- php方法重载
php方法重载 <?php/* * php面向对象的重写与重载重写:就是当子类继承父类的一些方法后,子类又在其内部定义了相同的方法,则这个新定义的方法会覆盖继承而来的父类的方法,子类只能调用 ...