问题:当有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;
}