火车最少钢轨数问题(概率算法实现,带详细注释C++代码)

66 篇文章 3 订阅

问题描述:

一个博物馆收藏了不同的火车,同地区的火车需要的铁路宽度不一样。现在这个博物馆为了陈列火车需要一些铁路,每条铁路可以看作是由两根平行钢轨组成的,只要把两条钢轨的距离摆放合适了,就可以放置对应的火车。
这个博物馆每次只展示一列火车,所以可以减少所需钢轨的数量。比如,如果有n列火车,他们需要的铁路宽度都不相同,那么只需要n+1条轨道就一定可以让每列火车都能够摆放。方法如下:把一条轨道放在最左边,剩下的n条钢轨里,第i条距离最左边轨道的距离和第i列火车需要的铁路宽度一样,这样n+1条铁轨就能够放下所有火车了。
【输入形式】
每个测试用例输入第一行包含n个整数n,表示火车所需要的铁路宽度的个数。接下来的一行有1<n<=8个1000-5000之间的整数,每个整数表示一个所需要的铁路宽度。
【输出形式】
对每个测试用例输出一个整数,表示所需要的钢轨数量。
【样例输入】
4
1524 1520 1609 1435
【样例输出】
4

今天算法实验考试正好考了这道题,当时完全不会写…考完试之后突然恍然大悟,用十分钟写完了下面的代码。

完整代码:

#include<iostream>
#include<vector>//需要使用到向量模板类
#include<set>//需要使用到集合对向量进行去重
using namespace std;

//采用概率算法,也就是通过循环得到多组可行解,判断可行解是否最优,当循环次数很大时由概率学知识可知当前最优解就是全局最优解
int Least_Num(const unsigned* Sizes, const unsigned& Num)
{
	int min_Num = 10000000;//定义一个变量表示当前的最优解,初始化为一个很大的数字
	for (int i = 0; i < 1000; i++)//循环一千次,每次循环得到一个可行解
	{
		vector<int>pos;//用一个向量记录当前的摆放位置
		pos.push_back(0);//首先在坐标原点处摆放一根铁轨
		for (unsigned j = 0; j < Num; j++)
		{
			loop:
			int Which_Num = rand() % pos.size();//通过随机数找出一根已经摆放了的钢轨的位置
			int direction = rand() % 2;//确定下一根安放的钢轨是在找出的钢轨的左边还是右边
			if (direction == 0)//方向为0表示在找出的钢轨的左边安放新钢轨
			{
				if (pos[Which_Num] - Sizes[j] >= 0)//如果安放的位置已经大于坐标远点的位置,则属于可以安放的位置,安放铁轨并将安放位置记录下来
				{
					pos.push_back(pos[Which_Num] - Sizes[j]);
				}
				else//如果想要安放的位置已经在原点的左边,那么就不合理了,此次循环无效,重复上述过程一遍(使用goto语句)
				{
					goto loop;
				}
			}
			else if (direction == 1)//方向为零表示在右边,此时安放位置没有限制,直接安放新的铁轨并记录位置即可
			{
				pos.push_back(pos[Which_Num] + Sizes[j]);
			}
		}
		//使用集合对安放位置进行去重操作
		set<int> tempSet(pos.begin(), pos.end());//此时所有钢轨的安放位置都已经确定,得到了一个可行解,但是可能存在铁轨的安放位置重合,那么就可以节约一根铁轨
		pos.assign(tempSet.begin(), tempSet.end());//将去重后的结果重新赋值给向量
		if (pos.size() < min_Num)//判断是否需要更新局部最优解
		{
			min_Num = pos.size();
		}
	}
	return min_Num;//返回当前的最优解
}

int main(void)
{
	unsigned TrainNum;
	cin >> TrainNum;
	unsigned* TrainSize = new unsigned[TrainNum];
	for (unsigned i = 0; i < TrainNum; i++)
	{
		cin >> TrainSize[i];
	}
	cout << Least_Num(TrainSize, TrainNum);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值