回溯算法—公路收费点重建问题

本文介绍了一种利用回溯算法解决公路收费点重建问题的方法。通过给定的距离集合,逐步推导出各点可能的位置坐标。文章详细解释了算法流程,包括初始化点位置、距离验证及回溯过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   问题:当有x1,x2,...xn点,它们位于x轴上。初始点的坐标x1=0,这些点是从左到右给出的,即i>j,则xi>xj。那么可得到两两点之间的距离。d1,d2,...dm。其中m=n(n-1)/2。现在的问题是已知到距离d1,d2,...dm,如何求解x1,x2,...xn?这个问题采用回溯算法实现。

比如例子:D={d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15}={1,1,2,2,2,3,3,4,4,5,5,6,7,7,9}。求解{x1,x2,x3,x4,x5,x6}

(1)、x1=0,显然x6=9,因为最大的距离为x6-x1,删除D中d=9,D={1,1,2,2,2,3,3,4,4,5,5,6,7,7}。

(2)D中最大的为7,也就是x5=7或者x2=2。其实2者的位置是对称的,要么都有解,要么两者都无解。选择x5=7,拭目以待,看看x2是否为2。

x5-x1=7;x6-x5=2出现在D中,所以满足条件。除去D中一个7一个2,得:D={1,1,2,2,3,3,4,4,5,5,6,7}。

(3)D中最大的为7,也就是x4=7或者x2=2。选择x4=7,x4-x0=7,x6-x4=2,x5-x4=0。0不在D中所以,x4=7不满足,那么就选择x2=2。x2-x1=2,x6-x2=7,x5-x2=5,在D中满足条件。除去D中的2,7,5。D={1,1,2,3,3,4,4,5,6}。

(4)D中最大的为6,也就是x4=6或者x3=3。选择x4=6,x4-x1=6,x4-x2=4,x5-x4=1,x6-x4=3,都在D中,满足条件。删除D中6  4  1 3;D={1,2,3,4,5}。                                             (5)D中最大的为5,剩下一个x3没赋值,x3=5或者x3=4;选择x3=5,x3-x1=5, x3-x2=3, x4-x3=1, x5-x3=2, x6-x3=4。满足D中的条件。删除5,3,1,2.4,此时D为空程序结束。求出: {x1,x2,x3,x4,x5}={0,2,5,6,7,9}。

其实在上边选择过程,(4)中若选择x3=3,则可得解:{x1,x2,x3,x4,x5,x6}={0,2,3,4,7,9}也满足解。在(4)中若选择x4=6满足解,(5)中不满足解,则此时应该回溯到(4)中重新选择分支,x3=3。

backtrack.h

#pragma once
#include<iostream>
#include<vector>
using namespace std;
class Backtrack
{
public:
	Backtrack();
	void insert(int dis);//把距离存储在vector中,并进行排序
	void remove(int dis);//删除某个元素
	bool contains(int dis);//判断是否包含某元素
	bool  turnpike();
	bool place(int n,int left,int right);//回溯法查找
	void show();
private:
	vector<int> x;//保存点
	vector<int> d;//保存距离
};


backtrack.cpp

#include "stdafx.h"
#include"backtrack.h"
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
Backtrack::Backtrack()
{
	d.push_back(0);
}
void Backtrack::insert(int dis)//将距离存储在vector中,并按由小到大的顺序排列。
{
	vector<int>::iterator it;
	for(it=d.begin();it!=d.end();++it)
	{
		if(*it>dis)
			break ;
	}
	d.insert(it,dis);
}
void Backtrack::remove(int dis)//删除指定的某个元素
{
	vector<int>::iterator it=d.begin();
	      ++it;
	for(;it!=d.end();++it)
	{
		if(*it==dis)
		{
			d.erase(it);
			return;
		}

	}
}
bool Backtrack::contains(int dis)
{
	vector<int>::iterator it=d.begin();
	    ++it;
	for(;it!=d.end();++it)
		if(*it==dis)
			return true;
	return false;
}
bool Backtrack::turnpike()
{
	int k=d.size()-1;
	int n;
	n=(1+sqrt((double)(1+8*k)))/2;//根据距离的数,计算出点的数
	x.resize(n+1);//让x从x[1]开始记录

	x[1]=0;//初始化第一个点的位置在0处
	x[n]=d[k];
	x[n-1]=d[k-1];
	remove(x[n]);//删除距离中为x[n]的距离
	remove(x[n-1]);//删除距离中为x[n-1]的距离
	bool flag;
	flag=contains(x[n]-x[n-1]);//判断x[n]-x[n-1]的距离是否在d中
	if(flag)          
	{
		remove(x[n]-x[n-1]);
		return place(n,2,n-2);
	}
	else
		return false;
}

bool Backtrack::place(int n,int left,int right)
{
	int dmax;
	bool found=false;
	bool flagR=true;
	bool flagL=true;
	int k=d.size()-1;
	if(k<=0)
		return true;
	dmax=d[k];    //取出最大的距离
	for(int i=1;i<left;i++)
	{
		flagR=flagR&&contains(dmax-x[i]);//若x[right]=dmax,判断1—left-1之间的点和x[right]的距离是否在d中
	}
	for(int i=right+1;i<=n;i++)
	{
		flagR=flagR&&contains(x[i]-dmax);//若x[right]=dmax,判断right+1—n之间的点和x[right]的距离是否在d中
	}
	for(int i=1;i<left;i++)
	{
		flagL=flagL&&contains(x[n]-dmax-x[i]);//若x[left]=x[n]-dmax,判断1—left-1之间的点和x[left]的距离是否在d中
	}
	for(int i=right+1;i<=n;i++)
	{
		flagL=flagL&&contains(x[i]-(x[n]-dmax));
	}

	if(flagR)                           //若在都d中,表明满足距离要求,将x[right]=dmax,删除1—left-1之间的点和x[right]的距离、
	{                                  //删除right+1—n之间的点和x[right]的距离
		x[right]=dmax;
		for(int i=1;i<left;i++)
			remove(x[right]-x[i]);
		for(int i=right+1;i<=n;i++)
		remove(x[i]-x[right]);
		found=place(n,left,right-1);//向下进行找点
		if(!found)  //若不满足要求,则回溯,将上面删除的距离点再次查入到d中
		{
			for(int i=1;i<left;i++)
				insert(x[right]-x[i]);
			for(int i=right+1;i<=n;i++)
				insert(x[i]-x[right]);
		} 
	}
	if(!found&&flagL)
	{
		x[left]=x[n]-dmax;
		for(int i=1;i<left;i++)
			remove(x[n]-dmax-x[i]);
		for(int i=right+1;i<=n;i++)
			remove(x[i]-(x[n]-dmax));
		found=place(n,left+1,right);
		if(!found)
		{
		   for(int i=1;i<left;i++)
			   insert(x[n]-dmax-x[i]);
		   for(int i=right+1;i<=n;i++)
			   insert(x[i]-(x[n]-dmax));
		}
	}
	return found;
}
void Backtrack::show()
{
	bool f=turnpike();
	if(f)
	{
		for(int i=1;i<x.size();i++)
			cout<<x[i]<<" ";
		cout<<endl;
	}
	else
		cout<<"no solution"<<endl;
}


Algorithm-backtrack1.cpp

// Algorithm-backtrack1.cpp : 定义控制台应用程序的入口点。
//
//回溯算法-公路收费点重建问题
#include "stdafx.h"
#include"backtrack.h"
#include<iostream>
#include<vector>
int _tmain(int argc, _TCHAR* argv[])
{
	Backtrack b;
	b.insert(1);
	b.insert(1);
	b.insert(3);
	b.insert(4);
	b.insert(7);
	b.insert(8);
	b.insert(9);
	b.insert(10);
	b.insert(10);
	b.insert(11);
	b.insert(11);
	b.insert(12);
	b.insert(13);
	b.insert(20);
	b.insert(21);
	b.show();
	return 0;
}


 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值