UVa Q10137: The Trip (旅行)

探讨了一道经典的算法题目——旅游团成员间费用均摊问题。通过分析得出最优解策略,并分享了解题过程中的常见误区及解决方案。

未经许可,请匆转载!!!

公司从去年开始注重各级别工程师在算法编码方面的功底,不仅经常组织TopCoder考试,还把考试等级作为每年绩效考评的重要参考(鄙人对此非常反感)。不过反感归反感,但每次考试,如想让代码通过自动在线评测系统,还是得在算法上花一些功夫的。由于之前项目组顾老大非常支持个人练习(狂赞),特地用项目经费买了本编程挑战的书。书到项目组快一年多了,最近才有时间借来看看,也尝试着做书上的题,发现还是挺有趣的,能学到不少平时不注意的东西,也能提前写程序的技巧。由于鄙人做的第一题在提交4次后终于被系统AC,回头细想下,经常做做这些uva的算法题对自己各方面也是个锻炼,特此对每做的算法题做一总结。

读者如若发现分析或者代码有误,或者有更高效,更巧妙的算法,请留言或者联系作者,不胜感激!

contact information:

name: hugehwang       E-mail:fantasynavy0817@126.com     Tele-phone: no


原题描述:

有一群学生团体准备去国外旅游,他们都同意费用均摊,但是在值钱的时候却很不方便。于是有人先付车钱,有人先付餐费等等。等到旅游结束后,再统计每个人出了多少钱,然后出得少的拿钱给出钱多的人,使得每个人所出的钱都相等,如果无法使每个人出的钱相等,那就要求只能相差在一分钱之内。你现在的任务是:给你每个学生的旅游时出的费用,请你算出最少有多少钱要交换才能使每个人出的钱相等或在允许的误差之内。

输入数据:

每组测试数据的第一行有一个数,它代表本次旅游的总人数。接下来的n行为每个学生旅游时出的钱,最小的单位为一分钱。学生的总人数不会超过1000人,每个人出的钱不会超过$10000.00元。n=0时表示输入结束。

输出数据:

对每组数据输出一行,输出最少需要交换多少钱就能使得每个人所出的钱尽可能接近。 格式请参考如下sample.

sample input

3

10.00

20.00

30.00

4

15.00

15.01

3.00

3.01

5

5000.00

11.11

11.11

11.11

11.11

0


sample output

$10.00

$11.99

$3991.11


分析:

初次接触此题,很容易就会想到解法为求平均值。但此题在求平均值的基础上增加了条件,那就是误差可以在1美分之内,其实本质上也就是平均值可以在0~1之间浮动。现在假设平均值下限值记为avg【向下取整】,那么平均值的上限值则可以记为avg+1【向上取整】。为了使每个人出的钱都接近相等,那就必须让每个人出的钱都尽可能地在这里定义的两个平均值之间,那么出的钱少于这个平均值的人所补的钱之和或者出的钱高于这个平均值的人所收的钱之和就是我们想要计算的总的要交换的钱了。为了让交换的钱尽可以地少,那就必须让要收钱的人尽可以少收钱或者要出钱的人尽可能少出钱。于是,要收钱的人以avg+1为基准而要出钱的人以avg为基准。这样就解决了我们的问题了。


#include 
  
   
#include 
   
    
using namespace std;

int money[1000];

int main()
{
	//freopen("H:\\algorithm_study\\uva_the_trip\\sample_input.txt", "r", stdin);
	//person in trip
	int persons;
	while (cin >> persons && persons)
	{
		int total = 0;
		//read money, in format of float
		for (int i = 0; i < persons; ++i)
		{
			int a = 0, b = 0;
			cin >> a;
			cin.get();
			cin >> b;
			money[i] = a * 100 + b;
			total += money[i];
		}

		//calculate average
		int avg = total / persons;
		int avg_add = avg + 1;

		//calculate minmize
		int remainder = 0;
		int diff = 0;
		int min = 0;
		for (int i = 0; i < persons; ++i)
		{
			if (money[i] > avg_add)
			{
				diff = money[i] - avg_add;
				remainder -= diff;
				min += diff;
			}
			else if (money[i] < avg)
			{
				diff = avg - money[i];
				remainder += diff;
			}
		}

		if (remainder > 0) 
		{
			min += remainder;
		}

		cout.setf(ios::fixed | ios::showpoint);
		cout.precision(2);
		cout << "$" << (double)min / 100 << endl;
	}

	return 0;
}

   
  

后记:

在解决此题的过程中,出现了5次在uva上提交不过的情况,最终发现是读取浮点数的方式不对。不过到现在我也没有明白为何先定义一个double型的变量d,然后再cin >> d;在uva上会不通过?在调试的过程中,发现如果要读取的浮点型数据是9999.99,那么d的值居然是9999.989999。怪哉!!!?

在网上也看了不少人的思路,很多人都是先由钱的总数整除总人数计算出一个平均值,其实这个是向下取整,然后以此平均值为基准计算大于这个平均值的人要收的钱的总数和少于这个平均值的人要出的钱的总数,再选出两个总数中的小的那个。由上面我们分析的可知,这种算法最小的不见得是正确的。另外,【以下见解纯属技术讨论,不含有任何诋毁、攻击等意图,如让读者产生误解,纯属意外!!!】对网上一个分析此题的想法印象特别深刻,大概是在这里所说的错误的思路的基础上再处理一个他认为是特殊的情况,数据由0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.03组成。虽然他给出的代码通过了uva的评测,但在我看来这种代码就是奔着结果去的,而且也不是很好理解。

算法的背后都是有着某种规律,可以由特殊推向一般,也可以由一般推向特殊。如果一种算法需要考虑对那些不具有特殊性的数据进行特殊处理的话,那么可以反证此算法是错误的。

以上文字纯属属个人研究所得,这里还是要感谢http://www.hankcs.com/program/uva-q10137-the-trip.html这篇文章的作者。让我正确的找到了平均的上限和下限,本以为下限是avg-1,上限是avg+1. 

欢迎大家指正!!!

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合群:具备一定Python编程基础和优化算法基础知识的高校学生、科研员及工程技术员,尤其适合从事工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值