Sicily1099-Packing Passengers-拓展欧几里德算法
最终代码地址:https://github.com/laiy/Datastructure-Algorithm/blob/master/sicily/1099.c
做这题的时候查了别人的做法花了半天都没搞明白怎么做的,我认为别的博客写的难以让人理解所以就造了这个轮子。
题目:
1099. Packing Passengers
Constraints
Time Limit: 1 secs, Memory Limit: 32 MB
Description
PTA, Pack ‘em Tight Airlines is attempting the seemingly impossible—to fly with only full planes and still make a profit. Their strategy is simplicity and efficiency. Their fleet consists of 2 types of equipment (airline lingo for airplanes). Type A aircraft cost costA dollars to operate per flight and can carry passengersA passengers. Type B aircraft cost costB dollars to operate per flight and can carry passengersB passengers.
PTA has been using software that works well for fewer than 100 passengers, but will be far too slow for the number of passengers they expect to have with larger aircraft. PTA wants you to write a program that fills each aircraft to capacity (in keeping with the name Pack 'em Tight) and also minimizes the total cost of operations for that route.
Input
The input file may contain data sets. Each data set begins with a line containing the integer n (1 <= n <= 2,000,000,000) which represents the number of passengers for that route. The second line contains costA and passengersA, and the third line contains costB and passengersB. There will be white space between the pairs of values on each line. Here, costA, passengersA, costB, and passengersB are all nonnegative integers having values less than 2,000,000,001.
After the end of the final data set, there is a line containing “0” (one zero) which should not be processed.
Output
For each data set in the input file, the output file should contain a single line formatted as follows:
Data set <N>: <A> aircraft A, <B> aircraft B
Where <N> is an integer number equal to 1 for the first data set, and incremented by one for each subsequent data set, <A> is the number of airplanes of type A in the optimal solution for the test case, and <B> is the number of airplanes of type B in the optimal solution. The 'optimal' solution is a solution that lets PTA carry the number of passengers specified in the input for that data set using only airplanes loaded to their full capacity and that minimizes the cost of operating the required flights. If multiple alternatives exist fitting this description, select the one that uses most airplanes of type A. If no solution exists for PTA to fly the given number of passengers, the out line should be formatted as follows:
Data set <N>: cannot be flown
Sample Input
600
30 20
20 40
550
1 13
2 29
549
1 13
2 29
2000000000
1 2
3 7
599
11 20
22 40
0
Sample Output
Data set 1: 0 aircraft A, 15 aircraft B
Data set 2: 20 aircraft A, 10 aircraft B
Data set 3: 11 aircraft A, 14 aircraft B
Data set 4: 6 aircraft A, 285714284 aircraft B
Data set 5: cannot be flown
题意就是求出passenger_A * x + passenger_B * y = passengers, 使得cost_A * x + cost_B * y最小。
我起初的想法是算出A和B哪个性价比大,然后取性价比大的那个最大的可能,再逐步递减到能够整除为止,这样做的效率是0.05s。
代码如下:
// Problem#: 1099
// Submission#: 4376506
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include <cstdio> inline int fix_upper(int upper, int passengers, int passenger_upper, int passenger_others) {
int temp = passengers - upper * passenger_upper;
while (temp % passenger_others) {
if (upper == )
return -;
upper--, temp += passenger_upper;
}
return upper;
} int main() {
int passengers;
int cost_A, passenger_A, cost_B, passenger_B;
int upper, others;
int count = ;
while (scanf("%d", &passengers) && passengers) {
scanf("%d %d", &cost_A, &passenger_A);
scanf("%d %d", &cost_B, &passenger_B);
if (passenger_A == && passenger_B == ) {
printf("Data set %d: cannot be flown\n", count++);
continue;
}
if ((passenger_A == && passenger_B != ) || (cost_A == && cost_B == && passenger_B > passenger_A)) {
if (passengers % passenger_B)
printf("Data set %d: cannot be flown\n", count++);
else
printf("Data set %d: %d aircraft A, %d aircraft B\n", count++, , passengers / passenger_B);
continue;
}
if ((passenger_B == && passenger_A != ) || (cost_A == && cost_B == && passenger_A > passenger_B)) {
if (passengers % passenger_A)
printf("Data set %d: cannot be flown\n", count++);
else
printf("Data set %d: %d aircraft A, %d aircraft B\n", count++, passengers / passenger_A, );
continue;
}
if (double(cost_A) / double(passenger_A) <= double(cost_B) / double(passenger_B)) {
upper = passengers / passenger_A;
upper = fix_upper(upper, passengers, passenger_A, passenger_B);
others = (passengers - upper * passenger_A) / passenger_B;
if (upper != -)
printf("Data set %d: %d aircraft A, %d aircraft B\n", count++, upper, others);
else
printf("Data set %d: cannot be flown\n", count++);
} else {
upper = passengers / passenger_B;
upper = fix_upper(upper, passengers, passenger_B, passenger_A);
others = (passengers - upper * passenger_B) / passenger_A;
if (upper != -)
printf("Data set %d: %d aircraft A, %d aircraft B\n", count++, others, upper);
else
printf("Data set %d: cannot be flown\n", count++);
}
}
return ;
}
写的很丑请见谅,我只是为了AC,我知道这样做并不好当时。
然后就来讲一下更好的做法:
我们要求的是x, y满足passenger_A * x + passenger_B * y = passengers。
拓展欧几里德算法其实就是在欧几里德算法求解过程中把x和y算出来了,具体是这样的:a * x + b * y = gcd(a, b)。
其实就是把上面等式的x和y求出来了,原理如下:
我们知道欧几里德算法原理是gcd(a, b) = gcd(b, a % b)不断递归下去,拓展欧几里德算法其实也是这个递归,只不过多加了一些x和y的赋值罢了。
假设在某一次递归过程中,a' = b, b' = a % b = a - a / b * b(C语言整数算法)。
那么gcd(a, b) = gcd(a', b') = a'x + b'y。
消去a', b'得到:ay +b(x - a / b * y) = Gcd(a, b)。
可以看到,这里的系数a的系数为y,b的系数为x - a / b * y。
通过这个原理递归我们最终得到的x和y就满足a * x + b * y = gcd(a, b)。
好,但是从这个算法也看不到和我们题目的关系是不是?
来看,首先有:passenger_A * x + passenger_B * y = gcd(passenger_A, passenger_B)
我们把上面的式子同时乘于passengers / gcd(passenger_A, passenger_B)试试:
passenger_A * (x * passengers / gcd(passenger_A, passenger_B)) + passenger_B * (y * passengers / gcd(passenger_A, passenger_B)) = passengers。
这个式子是不是就和passenger_A * x + passenger_B * y = passengers吻合了?
对应的x为(x * passengers / gcd(passenger_A, passenger_B)), y为(y * passengers / gcd(passenger_A, passenger_B))。
所以:我们先用欧几里德算法求出passenger_A * x + passenger_B * y = gcd(passenger_A, passenger_B)的x, y。
然后在通过变换得到passenger_A * x + passenger_B * y = passenger的x, y。
现在设这里求得的x, y为x0, y0。
这样就得到了满足以上等式一组的x0和y0的解了。
然后来看线型同余方程:
在数论中,线性同余方程是最基本的同余方程,“线性”表示方程的未知数次数是一次,即形如:
的方程。此方程有解当且仅当 b 能够被 a 与 n 的最大公约数整除(记作 gcd(a,n) | b)。这时,如果 x0 是方程的一个解,那么所有的解可以表示为:
其中 d 是a 与 n 的最大公约数。在模 n 的完全剩余系 {0,1,…,n-1} 中,恰有 d 个解。
所以,如果passengers不能整除gcd(passenger_A, passenger_B)则式子是无解的,若有解:
x = x0 + (passenger_B / gcd(passenger_A, passenger_B)) * k, y = y0 - (passenger_A / gcd(passenger_A, passenger_B)) * k, k为任意整数。
然后,根据题意,x >= 0, y >= 0。
所以有:
x0 + (passenger_B / gcd(passenger_A, passenger_B)) * k >= 0
=>
k >= (-x0) / (passenger_B / gcd(passenger_A, passenger_B))
y0 - (passenger_A / gcd(passenger_A, passenger_B)) * k >= 0
=>
k <= (y0) / (passenger_A / gcd(passenger_A, passenger_B))。
所以k的范围为:[(-x0) / (passenger_B / gcd(passenger_A, passenger_B)), (y0) / (passenger_A / gcd(passenger_A, passenger_B))]
然后来看:
我们的目标是cost_A * x + cost_B * y最小,带入x和y的表达式得:
cost_A * (x0 + (passenger_B / gcd(passenger_A, passenger_B)) * k) + cost_B * (y0 - (passenger_A / gcd(passenger_A, passenger_B)) * k)
消去x0, y0, gcd无关因素影响, 有:
cost_A * passenger_B * k - cost_B * passenger_A * k = k * (cost_A * passenger_B - cost_B * passenger_A)
好,那么就很简单了,如果 (cost_A * passenger_B - cost_B * passenger_A)为负,则k取最大值即可。
如果为正,k取最小值即可。然后得到k的值带入求出对应x和y即可。
代码如下:
#include <cstdio>
#include <cmath> inline long long gcd_extend(int &a, int b, long long *x, long long *y) {
static long long r, t;
if (b == ) {
*x = , *y = ;
return a;
} else {
r = gcd_extend(b, a % b, x, y);
t = *x;
*x = *y;
*y = t - a / b * *y;
return r;
}
} int main() {
int passengers;
int cost_A, passenger_A, cost_B, passenger_B;
int count = ;
long long lower, upper, k, gcd, x, y;
while (scanf("%d", &passengers) && passengers) {
scanf("%d %d", &cost_A, &passenger_A);
scanf("%d %d", &cost_B, &passenger_B);
gcd = gcd_extend(passenger_A, passenger_B, &x, &y);
if (passengers % gcd == ) {
x *= passengers / gcd;
y *= passengers / gcd;
upper = floor((double)y / (passenger_A / gcd));
lower = ceil((double)-x / (passenger_B / gcd));
k = passenger_B * cost_A - passenger_A * cost_B <= ? upper : lower;
printf("Data set %d: %lld aircraft A, %lld aircraft B\n", count++, x + (passenger_B / gcd) * k, y - (passenger_A / gcd) * k);
} else
printf("Data set %d: cannot be flown\n", count++);
}
return ;
}
这种做法效率为0.02s。
Sicily1099-Packing Passengers-拓展欧几里德算法的更多相关文章
- POJ 1061青蛙的约会(拓展欧几里德算法)
题目链接: 传送门 青蛙的约会 Time Limit: 1000MS Memory Limit: 65536K Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见 ...
- 欧几里德算法gcd及其拓展终极解释
这个困扰了自己好久,终于找到了解释,还有自己改动了一点点,耐心看完一定能加深理解 扩展欧几里德算法-求解不定方程,线性同余方程. 设过s步后两青蛙相遇,则必满足以下等式: (x+m*s)-(y+n ...
- POJ 2773 Happy 2006(欧几里德算法)
题意:给出一个数m,让我们找到第k个与m互质的数. 方法:这题有两种方法,一种是欧拉函数+容斥原理,但代码量较大,另一种办法是欧几里德算法,比较容易理解,但是效率很低. 我这里使用欧几里德算法,欧几里 ...
- (扩展欧几里德算法)zzuoj 10402: C.机器人
10402: C.机器人 Description Dr. Kong 设计的机器人卡尔非常活泼,既能原地蹦,又能跳远.由于受软硬件设计所限,机器人卡尔只能定点跳远.若机器人站在(X,Y)位置,它可以原地 ...
- 欧几里德与扩展欧几里德算法 Extended Euclidean algorithm
欧几里德算法 欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数. 基本算法:设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd( ...
- poj2142-The Balance(扩展欧几里德算法)
一,题意: 有两个类型的砝码,质量分别为a,b;现在要求称出质量为d的物品, 要用多少a砝码(x)和多少b砝码(y),使得(x+y)最小.(注意:砝码位置有左右之分). 二,思路: 1,砝码有左右位置 ...
- poj2115-C Looooops(扩展欧几里德算法)
本题和poj1061青蛙问题同属一类,都运用到扩展欧几里德算法,可以参考poj1061,解题思路步骤基本都一样.一,题意: 对于for(i=A ; i!=B ;i+=C)循环语句,问在k位存储系统中循 ...
- poj1061-青蛙的约会(扩展欧几里德算法)
一,题意: 两个青蛙在赤道上跳跃,走环路.起始位置分别为x,y. 每次跳跃距离分别为m,n.赤道长度为L.两青蛙跳跃方向与次数相同的情况下, 问两青蛙是否有方法跳跃到同一点.输出最少跳跃次数.二,思路 ...
- 【BZOJ-1965】SHUFFLE 洗牌 快速幂 + 拓展欧几里德
1965: [Ahoi2005]SHUFFLE 洗牌 Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 541 Solved: 326[Submit][St ...
随机推荐
- AOP学习过程中遇到的问题汇总
jdk版本问题: 在spring较低的版本中,仅支持jdk1.5到1.7版本,由于我本机安装的是jdk1.8,所以在调试的时候就会提示jdk版本要高于1.5.于是换成spring4.0,在co ...
- shipyard docker 管理平台
终于把shipyard弄好了. 我也是根据shipyard的官方文档,做的.在刚开始的时候觉得好难,也遇到了困难,查看了好多文档 但做完之后发现,只需要几步就能简单的配置成功,就能运行了. 修改tcp ...
- 服务器返回的JSON字符串
异步请求将type设为"json",或者利 用$.getJSON()方法获得服务器返回,那么就不需要eval()方法,因为这时候得到的结果已经是json对象
- OrderedDict
OrderedDict 使用dict时,Key是无序的.在对dict做迭代时,我们无法确定Key的顺序. 如果要保持Key的顺序,可以用OrderedDict: >>> from c ...
- datagridview用get,set访问并加锁,可以控制所有使用datagridview的地方都顺序进行访问
public System.Windows.Forms.DataGridView dataGridView1 { get { lock (ojb) { return dataGridView; } } ...
- 监听div内容改变
做前端突击队,外星人那道是自己手动模拟那个时间的变化的,但正确思路应该是监听div内容的变化然后同步到输入框中,遂今天找了一下,结果如下: $('div').bind('DOMNodeInserted ...
- PDF判断打印是A4还是B5
打印材料通畅就是这样两个规格,之前经常受其困扰,B5规格达成A4会显得字很大,当然本身A4就跟大:如果是A4打成B5字很小的: 其实,判断依据就是Adobe reader里面的,当鼠标滑向左下角的时候 ...
- gulp解决RequireJS
gulp解决RequireJS项目前端缓存问题(二) 前言 这一节,我们主要解决在上一节<使用gulp解决RequireJSs项目前端缓存问题(一)>末尾提到的几个问题: 对通过req ...
- Visual Studio 内置快速生产代码简写集合
工作之余,整理了一下,Visual Studio 里面的快速生产代码缩写集合,这个拿出来分享想一下,希望对您有所帮助. 文件下载地址:VS内置生产代码缩写集合文档.rar 首字母 简写 生成代码 a ...
- 【转载】JavaEE权限管理分析
JavaEE权限管理分析 一.背景 在 Web 应用开发中,安全一直是非常重要的一个方面.安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来.如果在应用开发的后期才考虑安全的问题,就可 ...