杭电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;
}


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