bzoj1021 [SHOI2008]Debt 循环的债务
2015-07-07 13:21
435 查看
前天打了一场比赛,让我知道自己Dp有多弱了,伤心了一天,没刷bzoj。
昨天想了一天,虽然知道几何怎么搞,但我还是不敢写,让我知道自己几何有多弱了,伤心了一天,没刷bzoj
Submit: 685 Solved: 350
[Submit][Status][Discuss]
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
5
输出二
0
分析:没写题意是因为这里有关于题意的转化,
一开始在思考债务的循环关系,十分复杂
有A->B->C这样的连环给的关系,但细想后发现
连环给钱的关系是不可能的,A->B->C肯定不如A->B,A->C这样好(对于同一种币值的货币来说)
根据欠债的关系,就能推断出目标状态的资金,对吧!?
初始资金能算出来,对吧!?
那。。。这不就是给出初始状态、结束状态,以及几种变化方式,求最少步数的类似问题!?
变化方式根据上面的分析,如果我们将每种币值的货币分开考虑,那么,就只有6种方式
A->B,C B->A,C C->A,B
AB->C AC->B BC->A
其中,A->B,C包含了 A->B,A->C两种(意思就是给0张货币也算给)
那么就可以得出Dp状态(其实类似于记忆化搜索)
Dp[i][M1][M2]表示用前i中货币,达到Alice有M1元,Bob有M2元,Cynthia有(Sum-M1-M2)元的状态的最少交换钱的张数
转移上面说了,整个过程相当于记忆化搜索
其中有个剪枝
如果某个人的现有钱数与目标钱数的差额(现有-目标)%剩下的币值的GCD(比如说现在算到第 i 种货币,那么就是 第 i+1种到第 6 种的币值的Gcd) != 零(不能整除)
那么显然不能大刀目标,因为剩下的币值都可以表示成 k*Gcd的形式,若不整除于Gcd,显然达不到
综上所述,此题得解
View Code
昨天想了一天,虽然知道几何怎么搞,但我还是不敢写,让我知道自己几何有多弱了,伤心了一天,没刷bzoj
1021: [SHOI2008]Debt 循环的债务
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 685 Solved: 350
[Submit][Status][Discuss]
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|。Source
题意:如题意所述分析:没写题意是因为这里有关于题意的转化,
一开始在思考债务的循环关系,十分复杂
有A->B->C这样的连环给的关系,但细想后发现
连环给钱的关系是不可能的,A->B->C肯定不如A->B,A->C这样好(对于同一种币值的货币来说)
根据欠债的关系,就能推断出目标状态的资金,对吧!?
初始资金能算出来,对吧!?
那。。。这不就是给出初始状态、结束状态,以及几种变化方式,求最少步数的类似问题!?
变化方式根据上面的分析,如果我们将每种币值的货币分开考虑,那么,就只有6种方式
A->B,C B->A,C C->A,B
AB->C AC->B BC->A
其中,A->B,C包含了 A->B,A->C两种(意思就是给0张货币也算给)
那么就可以得出Dp状态(其实类似于记忆化搜索)
Dp[i][M1][M2]表示用前i中货币,达到Alice有M1元,Bob有M2元,Cynthia有(Sum-M1-M2)元的状态的最少交换钱的张数
转移上面说了,整个过程相当于记忆化搜索
其中有个剪枝
如果某个人的现有钱数与目标钱数的差额(现有-目标)%剩下的币值的GCD(比如说现在算到第 i 种货币,那么就是 第 i+1种到第 6 种的币值的Gcd) != 零(不能整除)
那么显然不能大刀目标,因为剩下的币值都可以表示成 k*Gcd的形式,若不整除于Gcd,显然达不到
综上所述,此题得解
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <deque> #include <vector> #include <queue> #include <iostream> #include <algorithm> #include <map> #include <set> #include <ctime> using namespace std; typedef long long LL; #define For(i, s, t) for(int i = (s); i <= (t); i++) #define Ford(i, s, t) for(int i = (s); i >= (t); i--) #define Rep(i, t) for(int i = (0); i < (t); i++) #define Repn(i, t) for(int i = ((t)-1); i >= (0); i--) #define MIT (2147483647) #define INF (1000000001) #define MLL (1000000000000000001LL) #define sz(x) ((int) (x).size()) #define clr(x, y) memset(x, y, sizeof(x)) #define puf push_front #define pub push_back #define pof pop_front #define pob pop_back #define ft first #define sd second #define mk make_pair inline void SetIO(string Name) { string Input = Name+".in", Output = Name+".out"; freopen(Input.c_str(), "r", stdin), freopen(Output.c_str(), "w", stdout); } const int N = 1010, M[6] = {1, 5, 10, 20, 50, 100}; const int G[6] = {1, 5, 10, 10, 50, 100}; typedef pair<int, int> II; int Sum, Debt[3], St[3], Ed[3], Cash[3][6]; int Dp[2] , Visit[2] ; queue<II> Q[2]; int Ans = INF; inline void Input() { Rep(i, 3) scanf("%d", &Debt[i]); Rep(i, 3) Rep(j, 6) scanf("%d", &Cash[i][5-j]); } inline void Push(int w, int A, int B, int Step) { if(w >= 0) { int Last[3], C = Sum-A-B; Rep(i, 3) Last[i] = Ed[i]; Last[0] -= A, Last[1] -= B, Last[2] -= C; Rep(i, 3) if(Last[i]%G[w]) return; } int p = (w+2)%2; if(Visit[p][A][B] == w) Dp[p][A][B] = min(Dp[p][A][B], Step); else Q[p].push(mk(A, B)), Dp[p][A][B] = Step, Visit[p][A][B] = w; if(A == Ed[0] && B == Ed[1]) Ans = min(Ans, Step); } inline void Solve() { Rep(i, 3) { Rep(j, 6) St[i] += Cash[i][j]*M[j]; Sum += St[i], Ed[i] += St[i]; Ed[i] -= Debt[i], Ed[(i+1)%3] += Debt[i]; } Rep(i, 2) Rep(j, Sum) Rep(k, Sum) Visit[i][j][k] = -2; Push(-1, St[0], St[1], 0); int Last = 0, S[3], _Step, To, F1, F2, Front, T1, T2; Rep(w, 6) { Last ^= 1; while(sz(Q[Last])) { II State = Q[Last].front(); Q[Last].pop(); // One Get Front Two Rep(i, 3) { To = i, F1 = (i+1)%3; F2 = 0+1+2-F1-To; Rep(C1, Cash[F1][w]+1) Rep(C2, Cash[F2][w]+1) { S[0] = State.ft, S[1] = State.sd; S[2] = Sum-S[0]-S[1]; _Step = Dp[Last][S[0]][S[1]]+C1+C2; S[To] += (C1+C2)*M[w], S[F1] -= C1*M[w], S[F2] -= C2*M[w]; Push(w, S[0], S[1], _Step); } } // One Give To Two Rep(i, 3) { Front = i, T1 = (i+1)%3; T2 = 0+1+2-Front-T1; Rep(C1, Cash[Front][w]+1) Rep(C2, Cash[Front][w]-C1+1) { S[0] = State.ft, S[1] = State.sd; S[2] = Sum-S[0]-S[1]; _Step = Dp[Last][S[0]][S[1]]+C1+C2; S[Front] -= (C1+C2)*M[w]; S[T1] += C1*M[w], S[T2] += C2*M[w]; Push(w, S[0], S[1], _Step); } } } } if(Ans == INF) puts("impossible"); else printf("%d\n", Ans); } int main() { SetIO("1021"); Input(); Solve(); return 0; }
View Code
相关文章推荐
- maven基础篇
- NSString 转化 NSURL NSURL 转化 NSString
- ios-block回调
- 三:求二叉树的深度以及判断二叉树是否平衡
- Qt经典—线程、事件与Qobject
- git使用方法
- NYOJ 228 士兵杀敌系列
- 高级特性 反射
- JQuery Mobile开发中遇到的问题
- VS支持json后缀名的文件
- java中单例模式(饿汉式、懒汉式)的使用
- UESTC OJ 1647Battery Charging 模拟题
- js中escape,encodeURI,encodeURIComponent区别
- 64bit机器 C implicit-function-declaration 函数返回指针 地址值截断问题
- WordPress 4.3 Beta 1 全新发布,改进了后台功能和用户体验
- 修改IP地址
- 三味书屋 bbb
- 【每天读书半小时】7/1 - 7/3 JAVA网络编程
- Java生成指定长度并且包含大小写字母数字字符串示例
- OpenWrt arp 命令发布