【编程练习】TSP问题与最短路径

TSP
暴力枚举法:此方法不适合城市个数>8的。时间复杂度成阶乘上升

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxx 9999
int l[maxx][maxx];//存储两个城市之间的距离
int n;//城市数量
int min_l = maxx;//最短路径
int sum[maxx];//标记每条路线的路程总长度
int go_city;//标记从第go_city个城市出发
int visited[maxx]; //第i个城市已经去过:visited[i]=1;反之则为visited[i]=0;
int path_index = 1; //已经去过的城市数目。
int path[maxx][maxx];//存储经过城市的路线
int route = 0;//存储第几条路线
int recursion(int index)
{
    if(path_index != n)
    {
        for(int i=1;i <= n;i++)
        {
            if(visited[i] == 0)
            {
                visited[i] = 1;
                path[route][path_index] = index;
                path_index++;
                recursion(i);
                //回溯
                path_index--;
                visited[i] = 0;
            }
        }
    }
    else
    {
        //路线中加上最后一个城市和第一个城市(需要返回到最初的城市)
        path[route][path_index] = index;
        path[route][path_index + 1] = go_city;
        //计算每条路线的路程总长度,并输出路线
        printf("路线%d为:\n",route+1);
        sum[route] = 0;
        for(int i=1;i<=n;i++)
        {
            sum[route] += l[ path[route][i] ][ path[route][i+1] ];
            cout << path[route][i] << " --> ";
            //当route+1后,path[route][i]的前面需要保持,后面变化。
            path[route+1][i] = path[route][i];
        }
        if(min_l > sum[route])
        {
            min_l = sum[route];
        }
        cout << path[route][n+1] << endl;
        cout << "该路线总长度为: " << sum[route] << endl;
        route++;
    }
    return 0;
}

int main()
{
    memset(visited,0,sizeof(visited));
    cout << "请输入城市数量:";
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            printf("请输入%d号城市到%d号城市之间的距离:",i,j);
            cin >> l[i][j];
            l[j][i] = l[i][j];
        }
    }
    cout << "请输入您出发的城市是第几个城市:";
    cin >> go_city;
    visited[go_city] = 1;
    recursion(go_city);
    cout << "最短路程长度为: ";
    cout << min_l << endl;
    return 0;
}

动态规划法

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxx 10000
using namespace std;
int n;//城市数量
int l[maxx][maxx];//城市之间的距离
int dp[maxx][maxx];//dp表
int menthod()
{
	//给第一列赋值
	for (int i = 0; i < n; i++)
	{
		dp[i][0] = l[i][0];
	}
	//给其他列赋值
	for (int j = 1; j < 1 << (n - 1); j++)
	{
		for (int i = 0; i < n; i++)
		{
			dp[i][j] = 0x7ffff; //表示无穷大
			//判断是否走过该城市j,如果走过了,就continue
			if (j >> (i - 1) & 1)
			{
				continue;
			}
			for (int k = 1; k < n; k++)
			{
				//通过位运算判断是否需要经过k城市,不能经过k城市就continue
				if ((j >> (k - 1) & 1) == 0)
				{
					continue;
				}
				//获取dp[i][j]的最小值,(l[i][k] + dp[k][j ^ (1 << (k - 1))这个在思路上有解释
				if (dp[i][j] > (l[i][k] + dp[k][j ^ (1 << (k - 1))]))
				{
					dp[i][j] = l[i][k] + dp[k][j ^ (1 << (k - 1))];
				}
			}
		}
	}
	return 0;
}

int main()
{
	memset(dp, 0, sizeof(dp));
	cout << "请输入城市数目:";
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			printf("请输入第 %d 号城市到第 %d 号城市之间的距离:", i, j);
			cin >> l[i][j];
			l[j][i] = l[i][j];
		}
	}
	menthod();
	cout << "最短路径为: " << dp[0][(1 << (n - 1))-1] << endl;
	return 0;
}

此外还有遗传算法解决TSP问题,详见这条博客:
https://blog.csdn.net/weixin_44611644/article/details/95016984

最短路径问题
(1)所有点到点的
原理:对边进行“松弛”,v1到v3的距离 如果 大于v1 到v2+v2到v3 则更新v1-v3的最小值。
也就是说v1到v3的距离可以通过v2这个点进行更新。
Floyd:

int i,j,k;
for(k=0;k<n;k++)//代表中转结点
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
			{
				if(v[i][j]<v[i][k]+v[k][j])//v[i][j]表示结点i到j的距离
					v[i][j]=v[i][k]+v[k][j];
			}

o(n^3)可得到所有结点之间的最短路径长度。
核心代码如上,邻接矩阵的初始化和注意初始值取大数的问题。

单源最短路-迪杰斯特拉
假设一共n个点,可求得点x到剩余n-1个点的最短路。原理相似。
每次取出距离当前点最近的点,利用相邻点进行“松弛操作”更新dis数组。最终dis数组保存的值代表了结点0到剩余结点的最短距离。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAX=0x3f3f3f3f;
int map[110][110];
int dis[110];
int visit[110];
/*
关于三个数组:map数组存的为点边的信息,比如map[1][2]=3,表示1号点和2号点的距离为3
dis数组存的为起始点与每个点的最短距离,比如dis[3]=5,表示起始点与3号点最短距离为5
visit数组存的为0或者1,1表示已经走过这个点。
*/
int n,m;
int dijkstra()
{
    int i,j,pos=1,min,sum=0;
    memset(visit,0,sizeof(visit));//初始化为0,表示开始都没走过
    for(i=1; i<=n; i++)
    {
        dis[i]=map[1][i];
    }
    visit[1]=1;
    dis[1]=0;
    int T=n-1;
    while(T--)
    {
        min=MAX;
        for(j=1; j<=n; j++)
        {
            if(visit[j]==0&&min>dis[j])
            {
                min=dis[j];
                pos=j;
            }
        }
        visit[pos]=1;//表示这个点已经走过
        for(j=1; j<=n; j++)
        {
            if(visit[j]==0&&dis[j]>min+map[pos][j])//更新dis的值
                dis[j]=map[pos][j]+min;
        }
    }
    return dis[n];
}
int main()
{
    int i,j;
    while(cin>>n>>m)//n表示n个点,m表示m条边
    {
        memset(map,MAX,sizeof(map));
        int a,b,c;
        for(i=1; i<=m; i++)
        {
            cin>>a>>b>>c;
            if(c<map[a][b])//防止有重边
                map[a][b]=map[b][a]=c;
        }
        int sum=dijkstra();
        cout<<sum<<endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章