7-10 公路村村通
7-10 公路村村通(30 分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
思路:刚开始没有打算使用Dijkstra算法,直接使用神奇的五行代码,但是第四个测试点运行超时了;然后试了一下去掉
三重循环,改成Dijkstra,然而还是运行超时,我要去百度题解了😪
下面是超时的代码:
#include<stdio.h> #include<vector> #include<limits.h> #include<queue> #include<string.h> #include<iostream> using namespace std; #define MAXN 1001 int N, M, sum, map[MAXN][MAXN], flag[MAXN][MAXN]; bool empty_bfs(int begin) { int cnt = 0; //用来判断从begin开始能遍历的个数 queue<int>que; int flagg[MAXN]; memset(flagg, 0, sizeof(flagg)); que.push(begin); flagg[begin] = 1; while (!que.empty()){ int temp = que.front(); cnt++; que.pop(); for (int i = 1; i <= N; i++) if (!flagg[i] && map[temp][i] != 9999){ flagg[i] = 1; que.push(i); } } return (cnt == N ? true : false); } int main() { cin >> N >> M; //memset(map, 9999, sizeof(int)*MAXN*MAXN); for (int i = 1; i <= N; i++) for (int j = 1; j <= N; j++) map[i][j] = 9999; for (int i = 1; i <= N; i++) map[i][i] = 0; for (int i = 0; i < M; i++) { int a, b, c; cin >> a >> b >> c; map[a][b] = c; map[b][a] = c; } if (!empty_bfs(1) || M<N - 1){ //如果不是连通图则直接结束 cout << "-1" << endl; return 0; } //核心代码 for (int k = 1; k <= N;k++) for (int i = 1; i <= N;i++) for (int j = 1; j <= N; j++) { if (map[i][j] > map[i][k] + map[k][j]) map[i][j] = map[i][k] + map[k][j]; } memset(flag, 0, sizeof(flag)); for (int i = 1; i <= N; i++) { int min = INT_MAX, temp = 0; for (int j = 1; j <= N; j++) { if (i == j)continue; if (map[i][j] < min && !flag[i][j]) { min = map[i][j]; temp = j; } } for (int j = 1; j <= N; j++) { if (i == j || j == temp)continue; map[i][j] = 9999; } if (temp != 0){ flag[temp][i] = 1; flag[i][temp] = 1; sum += min; } } for (int i = 1; i <= N;i++) //将其变成无向图 for (int j = 1; j <= N; j++) { if (map[i][j] != 9999) map[j][i] = map[i][j]; } //核心代码 for (int k = 1; k <= N; k++) for (int i = 1; i <= N; i++) for (int j = 1; j <= N; j++) { if (map[i][j] > map[i][k] + map[k][j]) map[i][j] = map[i][k] + map[k][j]; } int max = 0; for (int i = 1; i <= N; i++) //寻找最大值将答案输出 for (int j = 1; j <= N; j++) { if (map[i][j] > max) max = map[i][j]; } cout << max << endl; return 0; }
#include<stdio.h> #include<vector> #include<limits.h> #include<queue> #include<string.h> #include<iostream> using namespace std; #define MAXN 1001 int N, M, sum, map[MAXN][MAXN], dis[MAXN][MAXN], flag[MAXN][MAXN]; void Dijkstra(int begin) { int flagg[MAXN]; memset(flagg, 0, sizeof(flagg)); for (int i = 1; i <= N; i++) dis[begin][i] = 9999; for (int i = 1; i <= N;i++) if (map[begin][i] != 9999)dis[begin][i] = map[begin][i]; dis[begin][begin] = 0; flagg[begin] = 1; int cnt = 1; while (cnt != N){ int min = 9999, temp; for (int i = 1; i <= N;i++) if (!flagg[i] && min > dis[begin][i]){ min = dis[begin][i]; temp = i; } flagg[temp] = 1; cnt++; for (int i = 1; i <= N; i++) if (!flagg[i] && map[temp][i] != 9999 && dis[begin][temp] + map[temp][i] < dis[begin][i]) dis[begin][i] = dis[begin][temp] + map[temp][i]; } } bool empty_bfs(int begin) { int cnt = 0; //用来判断从begin开始能遍历的个数 queue<int>que; int flagg[MAXN]; memset(flagg, 0, sizeof(flagg)); que.push(begin); flagg[begin] = 1; while (!que.empty()){ int temp = que.front(); cnt++; que.pop(); for (int i = 1; i <= N; i++) if (!flagg[i] && map[temp][i] != 9999){ flagg[i] = 1; que.push(i); } } return (cnt == N ? true : false); } int main() { cin >> N >> M; //memset(map, 9999, sizeof(int)*MAXN*MAXN); for (int i = 1; i <= N; i++) for (int j = 1; j <= N; j++) map[i][j] = 9999; for (int i = 1; i <= N; i++) map[i][i] = 0; for (int i = 0; i < M; i++) { int a, b, c; cin >> a >> b >> c; map[a][b] = c; map[b][a] = c; } if (!empty_bfs(1) || M<N - 1){ //如果不是连通图则直接结束 cout << "-1" << endl; return 0; } for (int i = 1; i <= N; i++) Dijkstra(i); memset(flag, 0, sizeof(flag)); for (int j = 1; j <= N; j++) { int min = INT_MAX, temp = 0; for (int i = 1; i <= N; i++) { if (i == j)continue; if (dis[i][j] < min && !flag[i][j]) { min = dis[i][j]; temp = i; } } map[j][temp] = dis[j][temp]; for (int i = 1; i <= N; i++) { if (i == j || i == temp)continue; map[i][j] = 9999; } if (temp != 0){ flag[temp][j] = 1; flag[j][temp] = 1; } } for (int i = 1; i <= N; i++) //将其变成无向图 for (int j = 1; j <= N; j++) { if (map[i][j] != 9999) map[j][i] = map[i][j]; } for (int i = 1; i <= N;i++) Dijkstra(i); int max = 0; for (int i = 1; i <= N;i++) for (int j = 1; j <= N;j++) if (dis[i][j] > max)max = dis[i][j]; cout << max << endl; return 0; }
学到了学到了,我居然一开始在用最短路径的方法来做最小生成树,要不是运行超时我都还不知道用prim算法😟
下面是AC代码:
#include<stdio.h> #include<limits.h> #include<queue> #include<string.h> #include<iostream> using namespace std; #define MAXN 1001 int N, M, map[MAXN][MAXN], visited[MAXN], dis[MAXN]; int prim() { int pos, min; //pos用来指向最小距离U中的点,min为权值 int ans = 0; memset(visited, 0, sizeof(visited)); //初始话U数组 visited[1] = 1; pos = 1; //将1加入数组中 for (int i = 2; i <= N; i++) dis[i] = map[pos][i]; for (int i = 1; i < N; i++) { min = INT_MAX; for (int j = 1; j <= N;j++) if (!visited[j] && min > dis[j]){ //寻找最小权值点 min = dis[j]; pos = j; } ans += min; visited[pos] = 1; for (int j = 1; j <= N;j++) //重新更新各点与已经在U中点的距离 if (!visited[j] && dis[j] > map[pos][j]) dis[j] = map[pos][j]; } return ans; } bool empty_bfs(int begin) { int cnt = 0; //用来判断从begin开始能遍历的个数 queue<int>que; int flag[MAXN]; memset(flag, 0, sizeof(flag)); que.push(begin); flag[begin] = 1; while (!que.empty()){ int temp = que.front(); cnt++; que.pop(); for (int i = 1; i <= N; i++) if (!flag[i] && map[temp][i] != INT_MAX){ flag[i] = 1; que.push(i); } } return (cnt == N ? true : false); } int main() { cin >> N >> M; for (int i = 1; i <= N;i++) //初始化图 for (int j = 1; j <= N; j++) if (i == j)map[i][j] = 0; else map[i][j] = INT_MAX; for (int i = 1; i <= M; i++) { int a, b, w; cin >> a >> b >> w; map[a][b] = w; map[b][a] = w; } if (N - 1> M || !empty_bfs(1)){ //如果不是连通图或边数少于N-1,则直接输出-1结束 cout << "-1" << endl; return 0; } cout << prim() << endl; return 0; }