旅行商问题
问题:有n个城市,已知任何两个城市之间的距离(或者费用),一个旅行商从某城市出发,经过每一个城市并且只经过一次,最后回到出发的城市,输出最短(或者费用最少)的线路。
背景:旅行商问题是一个经典的NP问题,不存在多项式时间内的解,使用暴力法时间复杂度将达到n!,但是可以使用动态规划来解,时间复杂度为2^n×(n×n)。(模板的TSP问题应该可以使用模拟退火算法解决,但是还没有写)。
思路:
对于暴力法,每次选择一个城市进行选择,然后枚举下一个城市,第一次选择次数为n,第二次选择次数为n-1,直到第n次为1个选择,时间为n*(n-1)…1=n!。
动态规划设计如下:
由于一个城市只有两种状态,即走或者不走,所以使用二进制来表示将节省大量,我们设dp[S][v]表示此时已走过的城市(或待走的城市)状态为S,其中S的二进制中第i为1则表示已走(或未走),为0则未走(或已走);其值为到达这种状态(即此时走过城市状态为S,目前在v号城市这种状态)所需的最少花费;例如此时有四个城市,S=6=二进制下的0100,此时已走过了三号城市,还未走一号二号四号城市。目的状态为dp[(1<<n)-1][0];(1<<n)-1的值为11…111,即连续n个1。
状态转移方程如下:
dp[0][0]=0;//表示目前一个城市也没有走,且当前在初始城市,此时花费为0
dp[S][v]=min(dp[S][v],dp[S-(1<<u)][u]);//其中u为S已走过的一个城市,在第u位上为1,表示走过城市状态为S,目前在v号城市这种状态从走过城市状态为S’,目前在u号城市这种状态经过一步u->v得到;其中S’为S-{u}。
代码如下:
import java.util.Arrays;
import java.util.Scanner;
public class Main2 {
static int[][] dis;
static int[][] dp;
static int n;
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner sc=new Scanner(System.in);
System.out.println("输入多少个城市 多少条道路:");
n=sc.nextInt();
dp=new int[1<<n][n];
dis=new int[n][n];
int m=sc.nextInt();
for(int i=0;i<n;i++) {
Arrays.fill(dis[i],10000);
dis[i][i]=0;
}
System.out.println("输入从 i 到 j 的值为 x");
for(int i=0;i<m;i++) {
int x=sc.nextInt();
int y=sc.nextInt();
int val=sc.nextInt();
dis[x][y]=dis[y][x]=val;
}
solve();
}
static void solve() {
for(int i=0;i<1<<n;i++)
Arrays.fill(dp[i],10000);
dp[0][0]=0;
for(int s=0;s<(1<<n);s++) { //枚举所有城市状态,可以证明大的状态一定由比它小的状态得到
for(int v=0;v<n;v++) { //枚举当前城市
for(int u=0;u<n;u++) { //枚举之前一步所在城市
if((s>>u&1)!=0) //之前一步必须为走过的城市,当前城市却可以是走过也可以是没走过
dp[s][v]=Math.min(dp[s][v],dp[s-(1<<u)][u]+dis[u][v]);
// System.out.println(s+" "+u+" "+dp[s][u]);
}
}
}
if(dp[(1<<n)-1][0]!=10000)
System.out.println(dp[(1<<n)-1][0]);
else
System.out.println("无解");
}
}
PS:其中v的选择既可以是走过的城市或者是未走过的城市,因为最后我们需要求dp[(1<<n)-1][0]的值,最后时候要回到初始节点,所以最后将会选择一个已走过的城市。
本文介绍了旅行商问题的背景和解决思路,这是一个经典的NP问题。通过动态规划方法,利用二进制状态压缩降低时间复杂度,最终达到2^n×(n×n)。文中给出了详细的状态转移方程和代码片段。
1118

被折叠的 条评论
为什么被折叠?



