货郎担问题也叫旅行商问题,即TSP问题(Traveling Salesman Problem),是数学领域中著名问题之一。
题目背景
有n个城市,用1,2,…,n表示,城i,j之间的距离为dij,有一个货郎从城1出发到其他城市一次且仅一次,最后回到城市1,怎样选择行走路线使总路程最短?
货郎担问题(TSP问题)是一个组合优化问题。
该问题可以被证明具有NPC计算复杂性。
经典模型
邮路问题
假定有一辆邮车要到n个不同的地点收集邮件,
这种情况可以用n十1个结点的图来表示。
一个结点表示此邮车出发并要返回的那个邮局,
其余的n个结点表示要收集邮件的n个地点。
由地点i到地点j的距离则由边 < i,j > 上所赋予的成本来表示。
邮车所行经的路线是一条周游路线,希望求出具有最小长度的周游路线。
螺帽问题
第二个例子是在一条装配线上用一个机械手去紧固待装配部件上的螺帽问题。
机械手由其初始位置(该位置在第一个要紧固的螺帽的上方)开始,
依次移动到其余的每一个螺帽,最后返回到初始位置。
机械手移动的路线就是以螺帽为结点的一个图中的一条周游路线。
一条最小成本周游路线将使这机械手完成其工作所用的时间取最小值。
生产安排问题
第三个例子是产品的生产安排问题。
假设要在同一组机器上制造n种不同的产品,生产是周期性进行的,
即在每一个生产周期这n种产品都要被制造。
要生产这些产品有两种开销,一种是制造第i种产品时所耗费的资金(1≤i≤n),称为生产成本,
另一种是这些机器由制造第i种产品变到制造第j种产品时所耗费的开支cij称为转换成本。
显然,生产成本与生产顺序无关。
于是,我们希望找到一种制造这些产品的顺序,
使得制造这n种产品的转换成本和为最小。
由于生产是周期进行的,因此在开始下一周期生产时也要开支转换成本,
它等于由最后一种产品变到制造第一种产品的转换成本。
于是,可以把这个问题看成是一个具有n个结点,边成本为cij图的货郎担问题。
动态规划解法
现在使用最广泛的还是动态规划的解法,但也只是适用于规模较小的情况
设计状态
f(i,S),表示从起点到i经过集合S中的所有点的最短路径
f(i,S)=min{f(j,S-{j})+dis(i,j)}
初始状态:f(i,{})=dis(起点,i)
最终答案:f(起点,{1,2,3,4,…,n-1})
时间复杂度:n^2*2^n
所以n只能<=15
代码很简单
用的是递归的思想
num:还没有经过的点的个数
now:当前位置
调用的时候默认start—>now的来路已经处理好了
递归处理now—>终点
枚举和now相连的所有点
如果枚举点i在来路上没有经过,而且不是start(不能在中途就回到起点了)
那么我们就继续递归
终止条件是:num==1,返回当前点到终点的距离
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int INF=1e9;
int n;
int dis[20][20];
int start=0;
bool p[20];
int doit(int num,int now) //num 剩余节点个数 now 当前节点
{
if (now==1)
return dis[now][start];
int minn=INF;
for (int i=0;i<n;i++)
if (!p[i]&&i!=start) //该点没有经过 且 不是起点
{
if (dis[now][i]+dis[i][start]>=minn) continue;
//从now经过i到达终点
p[i]=1;
int v=dis[now][i]+doit(num-1,i);
minn=min(minn,v);
p[i]=0;
}
return minn;
}
int main()
{
n=4;
int cc[4][4]={{0,4,1,3},
{4,0,2,1},
{1,2,0,5},
{3,1,5,0}};
//scanf("%d",&n);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
// scanf("%d",&dis[i][j]);
dis[i][j]=cc[i][j];
printf("%d",doit(n,start));
//ans=7;
return 0;
}