据说这是一道很裸的Prim模板题,网上的解释也很多。倔强的我在看了Prim生成原理后也决定自己动手写了。
STEP 1
已经相连的城市放在一个集合sele里,没选的放在队列remain里,然后每次都在remain里取出一个点,看看这个点与sele里的点哪个距离最小。输出。再把这个点归入sele。
#include <cstdio> #include <iostream> #include <cmath> #include <cstring> #include <queue> #include <algorithm> using namespace std; #define N 751 #define M 1001 int n, m; struct node { int x, y; double dis[N]; }city[N]; bool sele[N]; queue <int>remain; int ans; int main() { int i, j, count; double min; while(~scanf("%d", &n)) { count = 0; for(i = 1;i <= n;i++) scanf("%d%d", &city[i].x, &city[i].y); for(i = 1;i <= n;i++) for(j = 1;j <= n;j++) city[i].dis[j] = sqrt(pow(city[i].x-city[j].x,2)+pow(city[i].y-city[j].y,2)); scanf("%d", &m); memset(city, 0, n); while(m--) { scanf("%d%d", &i, &j); sele[i] = 1; sele[j] = 1; } for(i = 1;i <= n;i++) { if(!sele[i]) remain.push(i); } while(!remain.empty()) { min = 1e5; j = remain.front(); for(i = 1;i <= n;i++) { if(sele[i]) { double t = city[i].dis[j]; if(t < min) { min = t; ans = i; } } } sele[j] = 1; printf("%d %d\n", ans, j); remain.pop(); } } return 0; }万万没想到,这不是prim啊!prim取的是在sele里的所有点向外连的所有线段里的最小线段。而我却是在remain里选点,显然不是滴……
而且这样的算法还会漏掉几条边。比如说1-3,2-5连起来了,1,2,3,5都被放在了sele里。如果刚好1-4连好,一切OK,那么1-3-4在一起,2-5在一起,形成了两个集合,但是它们之间却不连通呢!
STEP 2
愚蠢的我为了解决不连通的缺陷,特别设置了数组链表vector vet[n],把同在一个集合里的城市放在一个链表里,并且用order记录每个城市所在的vet序号,只有vet[1]被当作sele,当有其他vet里的城市被连进vet[1]时,该vet里的城市都被连vet[1]。由于找min是O(N^3)时间复杂度,TLE了。
进步在于这用了prim思想,确实找到了vet[1]里的所有点向外连的所有线段里的最小线段。
#include <cstdio> #include <iostream> #include <cmath> #include <cstring> #include <queue> #include <algorithm> #include <vector> using namespace std; #define N 751 #define M 1001 int n, m; struct node { int x, y; double dis[N]; }city[N]; int order[N]; int main() { vector<int>vet[N]; int i, j, k, point; double min; while(~scanf("%d", &n)) { for(i = 1;i <= n;i++) scanf("%d%d", &city[i].x, &city[i].y); for(i = 1;i <= n;i++) for(j = 1;j <= n;j++) city[i].dis[j] = sqrt(pow(city[i].x-city[j].x,2)+pow(city[i].y-city[j].y,2)); scanf("%d", &m); for(i = 1;i <= n;i++) order[i] = 0; for(i = 1;i <= point;i++) while(!vet[i].empty()) vet[i].pop_back(); point = 1; int temp, big, change, len; if(m) { scanf("%d%d", &i, &j); order[i] = 1; order[j] = 1; vet[1].push_back(i); vet[1].push_back(j); m--; while(m--) { scanf("%d%d", &i, &j); if(!order[i] && !order[j]) { point++; order[i] = point, order[j] = point; vet[point].push_back(i); vet[point].push_back(j); } else if(order[i] && !order[j]) { order[j] = order[i]; vet[order[i]].push_back(j); } else if(!order[i] && order[j]) { order[i] = order[j]; vet[order[j]].push_back(i); } else { temp = order[i]<order[j]?order[i]:order[j]; big = max(order[i], order[j]); len = vet[big].size(); for(k = len - 1;k >= 0;k--) { change = vet[big][k]; order[change] = temp; vet[big].pop_back(); vet[temp].push_back(change); } } } } else { order[1] = 1; vet[1].push_back(1); } int count = vet[1].size(); int head, tail, num; while(count < n) { double min = 1e5; for(i = 0;i < count;i++) { num = vet[1][i]; for(j = 1;j <= n;j++) { if(order[j] != 1) { if(city[num].dis[j] < min) { min = city[num].dis[j]; head = num, tail = j; } } } } if(!order[tail]) { order[tail] = 1; vet[1].push_back(tail); } else { num = order[tail]; len = vet[num].size(); for(k = len - 1;k >= 0;k--) { change = vet[num][k]; vet[1].push_back(change); order[change] = 1; vet[num].pop_back(); } } printf("%d %d\n", head, tail); count = vet[1].size(); } } return 0; }
STEP 3
其实只要把可能要连入的边排序完了一遍再依次找就行了,所以又改进:
#include <cstdio> #include <iostream> #include <cmath> #include <cstring> #include <queue> #include <algorithm> #include <vector> using namespace std; #define N 751 #define M 1001 struct node { int x, y; long long dis[N]; }city[N]; struct nod { int x, y; long long dis; }ans[N*N]; bool cmp(nod a, nod b) { return a.dis < b.dis; } vector<int>vet[N]; int n, m, i, j, k, point, order[N]; int input() { for(i = 1;i <= n;i++) scanf("%d%d", &city[i].x, &city[i].y); for(i = 1;i <= n;i++) for(j = 1;j <= n;j++) city[i].dis[j] = pow(city[i].x-city[j].x,2)+pow(city[i].y-city[j].y,2); scanf("%d", &m); for(i = 1;i <= n;i++) order[i] = 0; for(i = 1;i <= point;i++) while(!vet[i].empty()) vet[i].pop_back(); } int bute() { int temp, big, change, len; if(!order[i] && !order[j]) { point++; order[i] = point, order[j] = point; vet[point].push_back(i); vet[point].push_back(j); } else if(order[i] && !order[j]) { order[j] = order[i]; vet[order[i]].push_back(j); } else if(!order[i] && order[j]) { order[i] = order[j]; vet[order[j]].push_back(i); } else { temp = order[i]<order[j]?order[i]:order[j]; big = max(order[i], order[j]); len = vet[big].size(); for(k = len - 1;k >= 0;k--) { change = vet[big][k]; order[change] = temp; vet[big].pop_back(); vet[temp].push_back(change); } } } int merge() { scanf("%d%d", &i, &j); order[i] = 1; order[j] = 1; vet[1].push_back(i); vet[1].push_back(j); m--; while(m--) { scanf("%d%d", &i, &j); bute(); } } int setsum() { m = 0; for(i = 1;i <= n;i++) for(j = i;j <= n;j++) if(i != j && !(order[i] == 1 && order[j] == 1)) { ans[m].x = i; ans[m].y = j; ans[m].dis = city[i].dis[j]; m++; } sort(ans, ans + m, cmp); } int main() { while(~scanf("%d", &n)) { int v = 0; input(); point = 1; if(!m) { order[1] = 1; vet[1].push_back(1); } else merge(); setsum(); for(k = 0;k < m;k++) { i = ans[k].x, j = ans[k].y; if(order[i] == order[j]) continue; bute(); printf("%d %d\n", i, j, v = 1); } if(!v) printf("\n"); } return 0; }竟然WA了....
STEP 4
经师兄指点,我的Prim理解错误,所以还是先看书吧( •̀ ω •́ )y ~~等我AC归来~~