Openjudge:4979:海賊王之偉大航路

思想 :

  • 類TSP問題 首先必須從起點0 出發 以n點爲終點
    在DP時 要把起點終點去掉 (否則會把“從k點到n點再到i點”等路徑算進去
    所以後面n-=2;

  • 其次 每個點經過且僅經過一次 所以用二進制 表示首尾之間的點是否走過
    0爲沒走過 1爲走過
    方程 f[s][i] 表示從0出發經過集合s中的點到i的最短時間
    f[s,i]=min(f[s,i],f[s-i,j]+dis[j,i]);
    邊界條件 f[1<<0,i]=輸入中給的從0到i的時間
    最後ans=min(ans,f[s][i]+dis[i][終點]);
    s表示除起點終點以外的點的集合 i爲其子集

#include <iostream>
#include <cstring>
using namespace std;
int f[(1<<14)][111];
int n,dis[36][36],ans=999999;
int main()
{
    memset(f,1,sizeof(f));
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>dis[i][j];
    n-=2;     //n-=2   
            //因爲題目要求從起點0出發,且以n爲終點 遍歷所有點     
        //所以n先減去一個起點  即把二進制表示中的第一個點從“表示起點0”變爲“表示點1”    (因爲0必須走  所以刪去二進制表示的起點0 防止重複更新
        ///因爲終點n也要去掉   所以n-=2;
    for(int i=0;i<n;i++)  //若n只減去1   此處for 的 i<n-1 保證 不用到終點
        f[(1<<i)][i]=dis[0][i+1];//這裏 i=0時 表示點1 而非起點0   i=1 爲點2.。。。
    for(int s=0;s<(1<<n);s++)  //(1<<N 二進制表示 集合  )枚舉集合 一直枚舉到 除起點 終點以外的所有的點
        for(int i=0;i<n;i++)
            if(s&(1<<i))   //i在集合中
            {
                for(int j=0;j<n;j++)
                {
                    if(i==j) continue;   
                    if(s&(1<<j))  //j在集合中
                        f[s][i]=min(f[s][i],f[s^(1<<i)][j]+dis[j+1][i+1]);//注意加1

                }
            }
    int s=(1<<n)-1;
    for(int i=0;i<n;i++)
    {
        ans=min(ans,f[s][i]+dis[i+1][n+1]);
    }
    cout<<ans<<endl;
}

http://blog.csdn.net/qq_18455665/article/details/50429230#comments

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