Programming Challenges 习题 4.6.5

PC/UVa:110405/10026

Shoemaker’s Problem

一个鞋匠有n个订单,一天只能处理一个订单,但是一个订单可能需要几天才能完成。对于第i个订单,Ti表示完成订单需要的时间,Si表示开始这个订单之前,每天需要付的罚金。求一个订单的安排顺序,使得罚金最少。

这道题目虽然写出来了,但我不知道为什么是对的。

题目就是求所有订单的全排列,然后输出罚金最少的。因为订单数量特别大,搜索的话时间复杂度极高,下一步就想用DP。DP思路和之前写过的2.8.8 Yahtzee思路类似,可以去参考那个。但是DP的空间复杂度一样非常大,最多有2^1000

突然我就想出来一个办法,在输入已有的排列下,交换相邻的两个订单,并不会对这两个之外的订单产生额外的罚金(因为对于其它的订单,开始工作之前的天数都没变),所以罚金的变化就在这两个上。那么如果把这两个交换一下,罚金可能会变少,所以就一直交换相邻的两个,直到整个序列的罚金不再变小。

看了一眼输出结果,这么做是对的,不过不知道为什么。。。

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	int cases = 0;
	cin >> cases;
	for (int c = 0; c < cases; c++)
	{
		int n = 0, t, s;
		cin >> n;
		vector<int> viT, viS, viNum;
		for (int i = 0; i < n; i++)
		{
			cin >> t;
			cin >> s;
			viT.push_back(t);
			viS.push_back(s);
			viNum.push_back(i);
		}
		while (1){
			bool bSwap = false;
			for (int i = 1; i < n; i++)
			{
				if (viT[viNum[i - 1]] * viS[viNum[i]] > viT[viNum[i]] * viS[viNum[i - 1]]){
					viNum[i - 1] ^= viNum[i] ^= viNum[i - 1] ^= viNum[i];
					bSwap = true;
				}
			}
			if (!bSwap) break;
		}
		for (size_t idx = 0; idx < viNum.size() - 1; idx++)
		{
			cout << viNum[idx] + 1 << ' ';
		}
		cout << viNum.back() + 1 << endl;
		if (c != cases - 1) cout << endl;
	}
	return 0;
}
/*
1

4
3 4
1 1000
2 2
5 5
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值