Programming Challenges 习题11.6.6

PC/UVa:111106/10261

Ferry Loading

汽车通过海峡时需要装载到船上,然后由船运抵对岸。每条船有两条相同长度的车道,单位为米,每辆车的长度不同,单位为厘米。给定一个等待上船的汽车队列,求最多可以装载多少辆车,以及装载方法。

这道题依然是线性结构的动态规划,但是有几个特别的地方:

  • 不需要排序
  • 轮到这辆车时,必须装载它,不能丢弃

我感觉这道题也可以不叫动态规划,就是一个使用递推代替穷举的题目。

在已经装载i辆车时,根据车的顺序,可以得到左右两车道的各种排列方式,以及每种排列方式中有多少辆车和总长度。当装载第i + 1辆车时,可以停在左车道,也可以停在右车道,或者两个车道都已经没有位置了(这时就得到了解为i辆车)。如果可以停在左车道,那么左车道的车辆数加1,总长度增加当前车的长度;如果可以停在右车道,那么左车道的车辆数目不变,总长度也不变。这道题也不需要合并重叠子问题,因为不能丢弃任意一辆车,所以没有什么状态是相同的。根据此递推关系,可以记录装载i辆车时,左车道的车辆长度(当然记录左车道装载了哪些车也可以,但是在装载下一辆车时需要通过计算来确定车道长度)。

装载每一辆车之前,枚举左车道所有可能的车道长度,同时判断右车道长度是否也是合理的:

  • 如果可以停到左车道,左车道长度更新,同时记录这辆车可以装载
  • 如果可以停到右车道,左车道长度不变(因此需要每次判断右车道长度是否满足要求),同时记录这辆车可以装载
  • 如果在左车道已有各种长度下,左右车道都没有空间了,则停止装载
#include <iostream>
#include <vector>
#include <string>
#include <sstream>

using namespace std;

void printChoice(const vector<vector<vector<bool>>> &Length,
	const vector<size_t> &vecCarLen,
	const size_t curr, const int leftLen)
{
	if (curr != 1){
		if (Length[curr][leftLen][1]){
			printChoice(Length, vecCarLen, curr - 1, leftLen - vecCarLen[curr - 1]);
		}
		else{
			printChoice(Length, vecCarLen, curr - 1, leftLen);
		}
	}
	if (Length[curr][leftLen][1]){
		cout << "port" << endl;
	}
	else cout << "starboard" << endl;
}

void loadCar(const vector<size_t> &vecCarLen, const size_t cm)
{
	vector<size_t> vecTotalLen;
	if (!vecCarLen.empty()) vecTotalLen.push_back(vecCarLen[0]);
	for (size_t idx = 1; idx < vecCarLen.size(); idx++)
	{
		vecTotalLen.push_back(vecTotalLen.back() + vecCarLen[idx]);
	}
	vector<vector<vector<bool>>> Length(vecCarLen.size() + 1, vector<vector<bool>>(cm + 1, vector<bool>(2, false)));
	Length[0][0][0] = true;
	Length[0][0][1] = true;
	size_t curr = 1;
	bool bCurr = false;
	for (; curr <= vecCarLen.size(); curr++)
	{
		bCurr = false;
		size_t currLen = vecCarLen[curr - 1];
		size_t currTotalLen = vecTotalLen[curr - 1];
		Length[curr] = Length[curr - 1];
		for (size_t len = 0; len < cm + 1; len++)
		{
			size_t lastRightLen = currTotalLen - currLen - len;
			if (Length[curr - 1][len][0] && lastRightLen <= cm){
				size_t newLen = len + currLen;
				if (newLen <= cm){//放左边
					Length[curr][newLen][0] = true;
					Length[curr][newLen][1] = true;
					bCurr = true;
				}
				if (currTotalLen - len <= cm){//放右边
					Length[curr][len][0] = true;
					Length[curr][len][1] = false;
					bCurr = true;
				}
			}
		}
		if (!bCurr) break;
	}
	if (curr > vecCarLen.size() || !bCurr){
		curr--;
	}
	cout << curr << endl;
	if (curr != 0){
		int leftLen = cm;
		while (leftLen > 0){
			if (Length[curr][leftLen][0]) break;
			leftLen--;
		}
		printChoice(Length, vecCarLen, curr, leftLen);
	}
}

int main()
{
	int T = 0;
	cin >> T;
	for (int t = 0; t < T; t++)
	{
		size_t meter;
		cin >> meter;
		cin.get();
		meter *= 100;
		vector<size_t> vecCarLen;
		string strLine;
		while (cin >> strLine){
			int CarLen;
			istringstream iss(strLine);
			iss >> CarLen;
			if (CarLen == 0) break;
			vecCarLen.push_back(CarLen);
		}
		loadCar(vecCarLen, meter);
		if (t != T - 1) cout << endl;
	}
	return 0;
}
/*
1

50
2500
3000
1000
1000
1500
700
800
0
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值