POJ 3311 Hie with the Pie(TSP模型+Floy+城市狀態進行壓縮+枚舉城市)

題目鏈接:https://vjudge.net/contest/374535#problem/G
The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortunately, due to cutbacks, they can afford to hire only one driver to do the deliveries. He will wait for 1 or more (up to 10) orders to be processed before he starts any deliveries. Needless to say, he would like to take the shortest route in delivering these goodies and returning to the pizzeria, even if it means passing the same location(s) or the pizzeria more than once on the way. He has commissioned you to write a program to help him.
Input
Input will consist of multiple test cases. The first line will contain a single integer n indicating the number of orders to deliver, where 1 ≤ n ≤ 10. After this will be n + 1 lines each containing n + 1 integers indicating the times to travel between the pizzeria (numbered 0) and the n locations (numbers 1 to n). The jth value on the ith line indicates the time to go directly from location i to location j without visiting any other locations along the way. Note that there may be quicker ways to go from i to j via other locations, due to different speed limits, traffic lights, etc. Also, the time values may not be symmetric, i.e., the time to go directly from location i to j may not be the same as the time to go directly from location j to i. An input value of n = 0 will terminate input.

Output
For each test case, you should output a single number indicating the minimum time to deliver all of the pizzas and return to the pizzeria.
Sample Input

3
0 1 10 10
1 0 1 2
10 1 0 10
10 2 10 0
0

Sample Output

8

翻譯:

一個人要到達1n總共n個地方,給出一個矩陣,表示任意兩個點間的距離,求從起點0到達n個點(到達指定的n個地點(1n))再回到起點0的最短時間。

TSP問題模型:
旅行商問題,即TSP問題(Traveling Salesman Problem)又譯爲旅行推銷員問題、貨郎擔問題,是數學領域中著名問題之一。假設有一個旅行商人要拜訪n個城市,他必須選擇所要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最後要回到原來出發的城市。路徑的選擇目標是要求得的路徑路程爲所有路徑之中的最小值。

分析:

  1. 鄰接矩陣存圖,Floy求任意兩點之間的距離
void Foly()
{
    for(int k=0; k<=n; k++)
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                if(mp[i][j]>mp[i][k]+mp[k][j])
                    mp[i][j]=mp[i][k]+mp[k][j];
}
  1. 進行狀態壓縮,從0出發,到達目標城市一路上經過的所有城市壓縮爲sta狀態
    dp[sta][i]dp[sta][i]:在sta的狀態下到達i的最短距離
    [0,1<<n)[0,1<<n)爲所有狀態的集合
  2. 在所有的狀態下,枚舉要到達的城市
for(int sta=0; sta<(1<<n); sta++)///枚舉所有的狀態
        {
            for(int i=1; i<=n; i++) ///所要到達的城市
            {
                if(1<<(i-1)==sta)///每一個城市的狀態爲1<<(i-1),只經過i城市,目標也爲i城市,最短路自然是mp[0][i]
                    dp[sta][i]=mp[0][i];
                else ///sta有經過多個城市
                {
                    dp[sta][i]=inf;
                    for(int k=1; k<=n; k++)
                    {
                        if(i!=k&&sta&(1<<(k-1)))///枚舉不是i的其他城市
                            dp[sta][i]=min(dp[sta][i],dp[sta^(1<<(i-1))][k]+mp[k][i]);///當前狀態下,不是i的其他城市,類似Floy枚舉中轉點
                    }
                }
            }
        }

完整代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1500;
const int inf=0x3f3f3f3f;
int mp[15][15];
int n;
int dp[N][15];///dp[sta][i]:在經過某些城市到達i城市的最短路,某些城市狀態壓縮用sta表示
void Foly()
{
    for(int k=0; k<=n; k++)
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                if(mp[i][j]>mp[i][k]+mp[k][j])
                    mp[i][j]=mp[i][k]+mp[k][j];
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                scanf("%d",&mp[i][j]);
        Foly();///求任意兩點之間的最短路
        memset(dp,0,sf(dp));
        for(int sta=0; sta<(1<<n); sta++)///枚舉所有的狀態
        {
            for(int i=1; i<=n; i++) ///所要到達的城市
            {
                if(1<<(i-1)==sta)///每一個城市的狀態爲1<<(i-1),只經過i城市,目標也爲i城市,最短路自然是mp[0][i]
                    dp[sta][i]=mp[0][i];
                else ///sta有經過多個城市
                {
                    dp[sta][i]=inf;
                    for(int k=1; k<=n; k++)
                    {
                        if(i!=k&&sta&(1<<(k-1)))///枚舉不是i的其他城市
                            dp[sta][i]=min(dp[sta][i],dp[sta^(1<<(i-1))][k]+mp[k][i]);///在沒經過城市i的狀態中,尋找合適的中間點k使得距離更短
                    }
                }
            }
        }
        int mi=dp[(1<<n)-1][1]+mp[1][0];///(1<<n)-1爲所有的狀態,即到城市1之前,所有的城市都已經到達
        for(int i=2; i<=n; i++)
            mi=min(mi,dp[(1<<n)-1][i]+mp[i][0]);
        printf("%d\n",mi);
    }
    return 0;
}

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