杭電oj 2544 固定起點終點最短路徑解題報告

注:鄙人最近按照此分類來刷題,假期的最低限度是刷掉所有的DP類,並且每一道題目寫一個解題報告,如果有志同道合的朋友,歡迎加QQ 823797837共同學習交流,也可以加羣ACM新手羣161986576,老鳥飛過



Problem Description
在每年的校賽裏,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?

 

Input
輸入包括多組數據。每組數據第一行是兩個整數N、M(N<=100,M<=10000),N表示成都的大街上有幾個路口,標號爲1的路口是商店所在地,標號爲N的路口是賽場所在地,M則表示在成都有幾條路。N=M=0表示輸入結束。接下來M行,每行包括3個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A與路口B之間有一條路,我們的工作人員需要C分鐘的時間走過這條路。
輸入保證至少存在1條商店到賽場的路線。
 

Output
對於每組輸入,輸出一行,表示工作人員從商店走到賽場的最短時間
 

Sample Input
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
 

Sample Output
3 2
 

剛開始用dp來搞,dp(i)表示以i爲起點到第N個頂點的最短路徑,求dp(i)的時候只需要求所有與i相鄰的結點j dp(j)最後,求a[i][j] + dp(j)最小的即可,不知道聰明的讀者是否發現這樣dp錯在哪裏呢?反正我是糾結了一個下午,後來幸虧小T給我舉了一個反例
5 5
 1 2 10
 1 3 4
 2 4 2
 3 4 3
 2 5 5
DP求dp(1)的時候 ,求完之後肯定要將路徑12,路徑13砍掉,不然下一次求dp(2), dp(3)的時候又會用到dp(1),無限遞歸了,看完下面這幅圖,你就什麼都明白了


#include <iostream>
using namespace std;

int a[101][101];
int vis[101][101];
int d[101];
int N, M, A, B, C;

int dp(int i) {
  if (i == N) return 0;
  int& ans = d[i];
  if (ans != -1) return d[i];
  ans = 20000000;

  for (int j = 1; j <= N; ++j) {
    if (a[i][j] > 0 && !vis[i][j]) { //如果i->j存在一條路並且這條路沒有走過的話
      vis[i][j] = vis[j][i] = 1; //將這條路刪除
      int t = a[i][j] + dp(j); //計算每一個 i->j的路徑 + j->...->N 的最短路徑
      if (ans > t) ans = t; //更新i->j->...->N的最短路徑
    }
  }
  return ans;
}
int main() {
  freopen("1.in", "r", stdin);
  while (scanf("%d%d", &N, &M) != EOF && (N != 0 || M != 0)) {
    memset(a, 0, sizeof(a));
    memset(vis, 0, sizeof(vis));
    memset(d, -1, sizeof(d));
    while (M--) {
      scanf("%d%d%d", &A, &B, &C);
      a[A][B] = a[B][A] = C;
    }
    cout << dp(1) << endl;
  }
  return 0;
}



最後很無奈地,選Dijkstra最短路徑算法,終於AC了
#include <iostream>
#define INF 20000000;
using namespace std;

int a[101][101];
int vis[101];
int p[101]; //p[j]表示從1到j的最短路徑
int N, M, A, B, C;

int dijkstra() {
  vis[1] = 1; //將1加入已求結點
  int min, min_index, cur = 1;

  for (int i = 2; i <= N; ++i)
    p[i] = a[1][i];

  for (int k = 1; k <= N-1; k++) {
    min = INF;

    //找到未求結點中離1最近的結點
    for (int i = 1; i <= N; ++i) {
      if (!vis[i] && a[cur][i] != -1 && min > p[i]) {
        min = p[i];
        min_index = i;
      }
    }
    vis[min_index] = 1; //將該結點加入已求結點
    cur = min_index; //記錄當前結點

    if (cur == N) return p[N];
    //以當前結點作爲中介更新未求結點的最短路徑
    for (int i = 1; i <= N; ++i) {
      if (!vis[i] && a[cur][i] != -1 && p[i] > p[cur] + a[cur][i])
        p[i] = p[cur] + a[cur][i];
    }
  }
  return p[N];
}
int main() {
  //freopen("1.in", "r", stdin);
  while (scanf("%d%d", &N, &M) != EOF && (N != 0 || M != 0)) {
    memset(a, -1, sizeof(a));
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= N; ++i) {
      for (int j = 1; j <= N; ++j)
        a[i][j] = INF;
      a[i][i] = 0;
    }
    while (M--) {
      scanf("%d%d%d", &A, &B, &C);
      a[A][B] = a[B][A] = C;
    }
    cout << dijkstra() << endl;
  }
  return 0;
}


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