算法訓練 Bus Tour


問題描述
  想象你是一個在Warsaw的游客,而且預訂了一次乘車旅行,去城鎮外看一些令人驚異的景點。這輛公共汽車首先圍繞城鎮行駛一段時間(一段很長的時間,由於Warsaw是一個大城市),把在各自旅館的人們帶上。接着它就去了那個令人驚異的景點,幾個小時后又回到城市中,再一次行駛到每一個旅館,這一次把乘客放下。
  由於某種原因,每當你這樣做的時候,你的旅館總是第一個要上車的,而且是最后一個才下車的,意味着你不得不忍受兩段經過所有當地旅館的不那么愉快的旅行。這很明顯不是你想要做的事(除非由於某種原因你真的要進入那些旅館),所以讓我們來做個改變。我們將開發一些軟件使得那些觀光公司能夠把它們的乘車旅行路線安排得更公平——盡管這有時候可能會導致每一個人的總距離更長,但公平就是公平,不是嗎?
  對於這個問題,有一個起始位置(觀光公司的總部),h個需要接送游客的旅館和一個目的地位置(令人驚異的景點)。我們需要找到一條路徑,從總部出發,經過所有的旅館,到景點去,再回來再一次經過所有的旅館(可能按照不同的順序),最后返回總部。為了保證沒有一個游客(特別是你)被迫忍受兩個完整的旅館旅行,我們要求在去景點的路上接游客的前 個旅館,在回來的路上也得是前 個讓游客下車的。受制於這些限制條件,我們想讓整個公車旅行盡可能短。注意這些限制條件可能會迫使公共汽車經過某個旅館但是不停下來(這不算做讓游客下車),后來再來這里讓游客下車,樣例就說明了這種情況。
輸入格式
  第一行包含兩個整數n和m滿足3≤n≤20,2≤m,n是位置的總數(旅館,總部和景點),m是汽車能在兩個位置之間行駛的路徑條數。
  n個不同的位置被標號為0到n-1,0是總部,1到n-2是旅館,而n-1是景點。假定任意一對位置之間最多只有一條直接路徑,而且從任意一個位置都能到達任意另一個位置(並不一定直接到達)。
  接下來m行,每行包含三個整數u,v和t,滿足0≤u,v≤n-1,u≠v,1≤t≤3600,表示公共汽車可以在t秒的時間內直接在u和v之間到達(兩個方向都可以)。
輸出格式
  一個整數,表示可能的最短路線的總耗時。
樣例輸入
5 4
0 1 10
1 2 20
2 3 30
3 4 40
樣例輸出
300
數據規模和約定
  對於20%的數據:n=3
  對於50%的數據:3≤n≤10
  對於100%的數據:3≤n≤20,2≤m
好久不刷題了,幫朋友做的,狀壓DP+最短路,算了一下復雜度超了。再想起來在寫吧。
思路:先floyd處理出來兩點之間的最短路 O(n^3) ,然后狀壓dp[v | (1 << j)][j] = min (dp[v | (1 << j)][j], dp[v][i] + dis[i][j]),枚舉狀態和兩個點。復雜度O(n*n*(1 << (n + 1)) 算了一下雖然10S,但是有點超。
/*************************************************************************
    > File Name: ALGO-176.cpp
    > Author: LyuCheng
    > Created Time: 2018-02-07 00:10
    > Description: 問題可拆分成:
        先處理出來從0訪問前h / 2個點 以第i個點為結尾的最短路,然后在枚舉
        n - 2個點作為起點,訪問后h / 2以 n - 1作為終點的最短路,然后這就
        是去景點的最短路,然后同理求從景點到起點的最短路
        狀態轉移方程:dp[v | (1 << j)][j] = min (dp[v | (1 << j)][j], dp[v][i] + dis[i][j])
 ************************************************************************/

#include <iostream>
#include <string>
#include <vector>
#include <string.h>

#define MAXN 21
#define INF 0x3f3f3f3f

using namespace std;

int n, m;
int u,v,w;
int mapn[MAXN][MAXN];
int dis[MAXN][MAXN];
int dp[(1 << MAXN)][MAXN]; //dp[i][j] 表示I狀態(訪問城市的狀態)下最后訪問j點的最短路的權值
int fdis[MAXN];//經過前h / 2個點的最短路
int edis[MAXN];//經過剩余點的最短路
vector <int> fir;
vector <int> sec;
int fres;
int sres;

void floyd() {
    memcpy(dis, mapn, sizeof mapn);
    for (int i = 0; i < n; ++ i) {
        for (int j = 0; j < n; ++ j) {
            for (int k = 0; k < n; ++ k) {
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
}

void addedge(int u, int v, int w) {
    if (mapn[u][v] > w) {
        mapn[u][v] = w;
        mapn[v][u] = w;
    }
}

void frist_half(int s) { //訪問前 h / 2 點的最短路
    memset(dp, INF, sizeof dp);
    dp[1][s] = 0;
    for (int v = 0; v < (1 << (n + 1)); ++ v) {
        for (int i = 0; i < n; ++ i) {
            for (int j = 0; j < n; ++ j) {
                    dp[v | (1 << j)][j] = min (dp[v | (1 << j)][j], dp[v][i] + dis[i][j]);
            }
        }
    }    
}

int end_half(int s, int t) { //訪問剩余的點的最短路
    memset(dp, INF, sizeof dp);
    dp[1][s] = 0;
    for (int v = 0; v < (1 << (n + 1)); ++ v) {
        for (int i = 0; i < n; ++ i) {
            for (int j = 0; j < n; ++ j) {
                    dp[v | (1 << j)][j] = min (dp[v | (1 << j)][j], dp[v][i] + dis[i][j]);
            }
        }
    }
    int ans = INF;
    for (int i = 0; i < (int) sec.size(); ++ i) {
        ans = min (ans, dp[sec[i]][t]);
    }
    return ans;
}

void init() {
    fres = INF;
    sres = INF;
    memset(mapn, 0, sizeof mapn);
    for (int i = 0; i < MAXN; ++ i) 
        for (int j = 0; j < MAXN; ++ j) 
            mapn[i][j] = (i == j) ? 0 : INF;
    fir.clear();
    sec.clear();
    for (int i = 0; i < (1 << (n + 1)); ++ i) {
        int s = 1;
        for (int j = 1; j < n / 2; ++ j) 
            if ((i & (1 << j)) == 0)
                s = 0;
        if (s == 1)
            fir.push_back(i);
        s = 1;
        for (int j = n / 2; j < n - 1; ++ j)
            if ((i & (1 << j)) == 0)
                s = 0;
        if (s == 1)
            sec.push_back(i);
    }
}

int main() {
//    freopen ("in.txt", "r", stdin);
    scanf ("%d %d", &n, &m);
    init();
    for (int i = 0; i < m; ++ i) {
        scanf ("%d %d %d", &u, &v, &w);
        addedge(u, v, w);
    }
    floyd(); // floyd處理出來兩點間的最短路    

    frist_half(0);
    memset(fdis, INF, sizeof fdis);
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j < (int) fir.size(); ++ j) 
            fdis[i] = min (fdis[i], dp[fir[j]][i]);    
    for (int i = 0; i < n; ++ i) 
        fres = min(fres, fdis[i] + end_half(i, n - 1));

    /*----------------------前半段處理完畢------------------------*/

    frist_half(n - 1);
    memset(edis, INF, sizeof edis);
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j < (int) fir.size(); ++ j)
            edis[i] = min (edis[i], dp[fir[j]][i]);    
    for (int i = 0; i < n; ++ i) { 
        int res = end_half(i, 0);
        sres  = min(sres, edis[i] + res);
    }

    printf ("%d\n", fres + sres);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM