注:鄙人最近按照此分类
来刷题,假期的最低限度是刷掉所有的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条商店到赛场的路线。
输入保证至少存在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
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;
}