【編程練習】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;
}

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