1、POJ 1251 Jungle Roads
题意:给出个村庄之间的道路及其维修费;从中选取一些路使道路的维修费最小且保证各村庄之间道路畅通。
思路:最小生成树模板。
①Kruskal
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 int n; 6 struct side 7 { 8 int v1; 9 int v2; 10 int len; 11 side(int vv1=0,int vv2=0,int ll=0):v1(vv1),v2(vv2),len(ll){} 12 friend bool operator <(const side&a,const side&b) 13 { 14 return a.len > b.len; 15 } 16 }; 17 vector<side>minHeap; 18 const int maxn = 30; 19 int pre[maxn]; 20 int Find(int x) 21 { 22 int r = x; 23 while (r != pre[r]) 24 { 25 r = pre[r]; 26 } 27 int c = x, p; 28 while (c != r) 29 { 30 p = pre[c]; 31 pre[c] = r; 32 c = p; 33 } 34 return r; 35 } 36 void Join(int x, int y) 37 { 38 int f1 = Find(x), f2 = Find(y); 39 if (f1 != f2) pre[f1] = f2; 40 } 41 int Kruskal() 42 { 43 side tmp; 44 int cnt = 1; 45 for (int i = 0; i <= n; i++)pre[i] = i; 46 int ans = 0; 47 while (cnt < n) 48 { 49 pop_heap(minHeap.begin(), minHeap.end()); 50 tmp = minHeap.back(); 51 minHeap.pop_back(); 52 int u = Find(tmp.v1); 53 int v = Find(tmp.v2); 54 if (u != v) 55 { 56 Join(tmp.v1, tmp.v2); 57 ans += tmp.len; 58 cnt++; 59 } 60 } 61 return ans; 62 } 63 64 int main() 65 { 66 while (~scanf("%d", &n)) 67 { 68 if (n == 0)break; 69 minHeap.clear(); 70 char v1, v2; 71 int len; 72 int k; 73 for (int i = 0; i < n - 1; i++) 74 { 75 76 cin >> v1; 77 scanf("%d", &k); 78 for (int j = 0; j < k; j++) 79 { 80 cin >> v2; 81 scanf("%d", &len); 82 minHeap.push_back(side(v1 - 'A', v2 - 'A', len)); 83 } 84 } 85 make_heap(minHeap.begin(), minHeap.end()); 86 int ans=Kruskal(); 87 printf("%d ", ans); 88 } 89 return 0; 90 }
②Prime
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 int n; 6 const int maxn = 30; 7 struct node 8 { 9 int to; 10 int len; 11 node(int tt=0,int ll=0):to(tt),len(ll){ } 12 friend bool operator <(const node&a, const node&b) 13 { 14 return a.len > b.len; 15 } 16 }; 17 vector<node>mp[maxn]; 18 vector<node>minheap; 19 bool vis[maxn]; 20 bool Cmp(int a, int b) 21 { 22 return a > b; 23 } 24 int Prime() 25 { 26 memset(vis, 0, sizeof(vis)); 27 minheap.clear(); 28 int cnt = 1; 29 vis[0] = true; 30 int sz = mp[0].size(); 31 for (int i = 0; i < sz; i++) 32 { 33 minheap.push_back(mp[0][i]); 34 } 35 make_heap(minheap.begin(), minheap.end()); 36 node tmp; 37 int ans = 0; 38 while (cnt < n) 39 { 40 do 41 { 42 pop_heap(minheap.begin(), minheap.end()); 43 tmp = minheap.back(); 44 minheap.pop_back(); 45 } while (vis[tmp.to]); 46 ans += tmp.len; 47 cnt++; 48 int u = tmp.to; 49 vis[u] = true; 50 int sz = mp[u].size(); 51 for (int i = 0; i < sz; i++) 52 { 53 if (vis[mp[u][i].to])continue; 54 minheap.push_back(mp[u][i]); 55 push_heap(minheap.begin(), minheap.end()); 56 } 57 58 } 59 return ans; 60 } 61 int main() 62 { 63 while (~scanf("%d", &n)) 64 { 65 if (n == 0)break; 66 for (int i = 0; i <= n; i++) mp[i].clear(); 67 char v1, v2; 68 int len; 69 int k; 70 for (int i = 0; i < n - 1; i++) 71 { 72 73 cin >> v1; 74 scanf("%d", &k); 75 for (int j = 0; j < k; j++) 76 { 77 cin >> v2; 78 scanf("%d", &len); 79 mp[v1 - 'A'].push_back(node(v2 - 'A', len)); 80 mp[v2 - 'A'].push_back(node(v1 - 'A', len)); 81 } 82 } 83 int ans = Prime(); 84 printf("%d ", ans); 85 } 86 return 0; 87 }
2、POJ 1287 Networking
题意:找出连接所有电脑的最短距离。
思路:求最小生成树的总权值。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 int n,m; 6 const int maxn = 55; 7 int pre[maxn]; 8 struct side 9 { 10 int v1; 11 int v2; 12 int len; 13 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 14 { 15 } 16 friend bool operator <(const side&a, const side&b) 17 { 18 return a.len > b.len; 19 } 20 }; 21 vector<side>minHeap; 22 int Find(int x) 23 { 24 int r = x; 25 while (r != pre[r]) 26 { 27 r = pre[r]; 28 } 29 int c = x, p; 30 while (c != r) 31 { 32 p = pre[c]; 33 pre[c] = r; 34 c = p; 35 } 36 return r; 37 } 38 void Join(int x, int y) 39 { 40 int f1 = Find(x), f2 = Find(y); 41 if (f1 != f2) pre[f1] = f2; 42 } 43 int Kruskal() 44 { 45 side tmp; 46 int cnt = 1; 47 for (int i = 0; i <= n; i++)pre[i] = i; 48 int ans = 0; 49 while (cnt < n) 50 { 51 pop_heap(minHeap.begin(), minHeap.end()); 52 tmp = minHeap.back(); 53 minHeap.pop_back(); 54 int u = Find(tmp.v1); 55 int v = Find(tmp.v2); 56 if (u != v) 57 { 58 Join(tmp.v1, tmp.v2); 59 ans += tmp.len; 60 cnt++; 61 } 62 } 63 return ans; 64 } 65 int main() 66 { 67 while (~scanf("%d", &n)) 68 { 69 if (n == 0)break; 70 minHeap.clear(); 71 int v1, v2; 72 int len; 73 int k; 74 scanf("%d", &m); 75 for (int i = 0; i < m; i++) 76 { 77 scanf("%d%d%d", &v1, &v2, &len); 78 minHeap.push_back(side(v1, v2, len)); 79 } 80 make_heap(minHeap.begin(), minHeap.end()); 81 int ans = Kruskal(); 82 printf("%d ", ans); 83 } 84 return 0; 85 }
3、POJ 2031 Building a Space Station
题意:空间站可以看成是圆球体,是建立在三维坐标系中的,给出N个空间站的(x,y,z)坐标及其半径r。如果两个空间站之间有接触(两球相交或相切),那么这两个空间站可以互相直接到达,否则(两球相离)需要在他们之间建立道路(道路长度为圆心距离减去两圆的半径)来连接。计算出将所有空间站连接起来所需要的最短路程。
思路:以每个空间站为节点,它们之间的距离为边权,建立无向图,求最小生成树。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 int n; 7 const int maxn = 105; 8 int pre[maxn]; 9 struct side 10 { 11 int v1; 12 int v2; 13 double len; 14 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 15 { 16 } 17 friend bool operator <(const side&a, const side&b) 18 { 19 return a.len > b.len; 20 } 21 }; 22 vector<side>minHeap; 23 int Find(int x) 24 { 25 int r = x; 26 while (r != pre[r]) 27 { 28 r = pre[r]; 29 } 30 int c = x, p; 31 while (c != r) 32 { 33 p = pre[c]; 34 pre[c] = r; 35 c = p; 36 } 37 return r; 38 } 39 void Join(int x, int y) 40 { 41 int f1 = Find(x), f2 = Find(y); 42 if (f1 != f2) pre[f1] = f2; 43 } 44 double Kruskal() 45 { 46 side tmp; 47 int cnt = 1; 48 for (int i = 0; i <= n; i++)pre[i] = i; 49 double ans = 0; 50 while (cnt < n) 51 { 52 pop_heap(minHeap.begin(), minHeap.end()); 53 tmp = minHeap.back(); 54 minHeap.pop_back(); 55 int u = Find(tmp.v1); 56 int v = Find(tmp.v2); 57 if (u != v) 58 { 59 Join(tmp.v1, tmp.v2); 60 ans += tmp.len; 61 cnt++; 62 } 63 } 64 return ans; 65 } 66 struct node 67 { 68 double x; 69 double y; 70 double z; 71 double r; 72 }points[maxn]; 73 int main() 74 { 75 while (~scanf("%d", &n)) 76 { 77 if (n == 0)break; 78 minHeap.clear(); 79 for (int i = 0; i < n; i++) 80 { 81 scanf("%lf%lf%lf%lf", &points[i].x, &points[i].y, &points[i].z,&points[i].r); 82 } 83 for (int i = 0; i < n; i++) 84 { 85 for (int j = i + 1; j < n; j++) 86 { 87 double len = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y) + (points[i].z - points[j].z)*(points[i].z - points[j].z))-points[i].r-points[j].r; 88 if (len < 0) len = 0; 89 minHeap.push_back(side(i, j, len)); 90 } 91 } 92 make_heap(minHeap.begin(), minHeap.end()); 93 double ans = Kruskal(); 94 printf("%.3lf ", ans); 95 } 96 return 0; 97 }
4、hdu 1102 Constructing Roads
题意:有n个村庄,编号1-n,以矩阵的形式给出任意两个村庄之间的距离,然后告诉已经有q个村庄已经修好了路,问现在要打算使所有村庄都联通需要修路的最小长度。
思路:把已经修好路的村庄直接并。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 int n; 7 const int maxn = 105; 8 int pre[maxn]; 9 int mp[maxn][maxn]; 10 struct side 11 { 12 int v1; 13 int v2; 14 double len; 15 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 16 { 17 } 18 friend bool operator <(const side&a, const side&b) 19 { 20 return a.len > b.len; 21 } 22 }; 23 vector<side>minHeap; 24 int Find(int x) 25 { 26 int r = x; 27 while (r != pre[r]) 28 { 29 r = pre[r]; 30 } 31 int c = x, p; 32 while (c != r) 33 { 34 p = pre[c]; 35 pre[c] = r; 36 c = p; 37 } 38 return r; 39 } 40 bool Join(int x, int y) 41 { 42 int f1 = Find(x), f2 = Find(y); 43 if (f1 != f2) 44 { 45 pre[f1] = f2; 46 return false; 47 } 48 else return true; 49 } 50 double Kruskal(int cnt) 51 { 52 side tmp; 53 int ans = 0; 54 while (cnt < n) 55 { 56 pop_heap(minHeap.begin(), minHeap.end()); 57 tmp = minHeap.back(); 58 minHeap.pop_back(); 59 int u = Find(tmp.v1); 60 int v = Find(tmp.v2); 61 if (u != v) 62 { 63 Join(tmp.v1, tmp.v2); 64 ans += tmp.len; 65 cnt++; 66 } 67 } 68 return ans; 69 } 70 int main() 71 { 72 while (~scanf("%d", &n)) 73 { 74 minHeap.clear(); 75 for (int i = 1; i <= n; i++) 76 { 77 for (int j = 1; j <= n; j++) 78 { 79 scanf("%d", &mp[i][j]); 80 if (mp[i][j] > 0) minHeap.push_back(side(i, j, mp[i][j])); 81 } 82 } 83 int q,cnt=1; 84 scanf("%d", &q); 85 for (int i = 0; i <= n; i++)pre[i] = i; 86 for (int i = 0; i < q; i++) 87 { 88 int u, v; 89 scanf("%d%d", &u, &v); 90 if (!Join(u, v)) cnt++; 91 } 92 make_heap(minHeap.begin(), minHeap.end()); 93 int ans = Kruskal(cnt); 94 printf("%d ", ans); 95 } 96 return 0; 97 }
5、zoj 1586 QS Network
题意:首先给一个t,代表t个测试样例,再给一个n,表示有n个QS装置,接下来一行是n个QS装置的成本。接下来是n*n的矩阵,表示每两个QS 装置之间链接需要的费用。求在全联通的情况下求最少费用。
思路:建立边时,边权值为两装置之间的连接费用+2装置的成本。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 int n; 7 const int maxn = 1005; 8 int pre[maxn]; 9 int adp[maxn]; 10 int mp[maxn][maxn]; 11 struct side 12 { 13 int v1; 14 int v2; 15 double len; 16 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 17 { 18 } 19 friend bool operator <(const side&a, const side&b) 20 { 21 return a.len > b.len; 22 } 23 }; 24 vector<side>minHeap; 25 int Find(int x) 26 { 27 int r = x; 28 while (r != pre[r]) 29 { 30 r = pre[r]; 31 } 32 int c = x, p; 33 while (c != r) 34 { 35 p = pre[c]; 36 pre[c] = r; 37 c = p; 38 } 39 return r; 40 } 41 bool Join(int x, int y) 42 { 43 int f1 = Find(x), f2 = Find(y); 44 if (f1 != f2) 45 { 46 pre[f1] = f2; 47 return false; 48 } 49 else return true; 50 } 51 double Kruskal() 52 { 53 side tmp; 54 int cnt = 1; 55 int ans = 0; 56 for (int i = 0; i <= n; i++) pre[i] = i; 57 while (cnt < n) 58 { 59 pop_heap(minHeap.begin(), minHeap.end()); 60 tmp = minHeap.back(); 61 minHeap.pop_back(); 62 int u = Find(tmp.v1); 63 int v = Find(tmp.v2); 64 if (u != v) 65 { 66 Join(tmp.v1, tmp.v2); 67 ans += tmp.len; 68 cnt++; 69 } 70 } 71 return ans; 72 } 73 int main() 74 { 75 int t; 76 scanf("%d", &t); 77 while (t--) 78 { 79 scanf("%d", &n); 80 minHeap.clear(); 81 for (int i = 1; i <= n; i++) scanf("%d", &adp[i]); 82 for (int i = 1; i <= n; i++) 83 { 84 for (int j = 1; j <= n; j++) 85 { 86 scanf("%d", &mp[i][j]); 87 if (i!=j) minHeap.push_back(side(i, j, mp[i][j]+adp[i]+adp[j])); 88 } 89 } 90 make_heap(minHeap.begin(), minHeap.end()); 91 int ans = Kruskal(); 92 printf("%d ", ans); 93 } 94 return 0; 95 }
6、poj 1789 Truck History
题意:用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的distance,现在要找出一个“衍生”方案,使得总代价最小,也就是distance之和最小。
思路:算出两两之间的dis,建边求最小生成树。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 int n; 7 const int maxn = 2005; 8 int pre[maxn]; 9 char mp[maxn][10]; 10 struct side 11 { 12 int v1; 13 int v2; 14 double len; 15 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 16 { 17 } 18 friend bool operator <(const side&a, const side&b) 19 { 20 return a.len > b.len; 21 } 22 }; 23 vector<side>minHeap; 24 int Find(int x) 25 { 26 int r = x; 27 while (r != pre[r]) 28 { 29 r = pre[r]; 30 } 31 int c = x, p; 32 while (c != r) 33 { 34 p = pre[c]; 35 pre[c] = r; 36 c = p; 37 } 38 return r; 39 } 40 bool Join(int x, int y) 41 { 42 int f1 = Find(x), f2 = Find(y); 43 if (f1 != f2) 44 { 45 pre[f1] = f2; 46 return false; 47 } 48 else return true; 49 } 50 double Kruskal() 51 { 52 side tmp; 53 int cnt = 1; 54 int ans = 0; 55 for (int i = 0; i <= n; i++) pre[i] = i; 56 while (cnt < n) 57 { 58 pop_heap(minHeap.begin(), minHeap.end()); 59 tmp = minHeap.back(); 60 minHeap.pop_back(); 61 int u = Find(tmp.v1); 62 int v = Find(tmp.v2); 63 if (u != v) 64 { 65 Join(tmp.v1, tmp.v2); 66 ans += tmp.len; 67 cnt++; 68 } 69 } 70 return ans; 71 } 72 int main() 73 { 74 while (~scanf("%d", &n)) 75 { 76 if (n == 0) break; 77 minHeap.clear(); 78 for (int i = 1; i <= n; i++) 79 { 80 scanf("%s", mp[i]); 81 } 82 for (int i = 1; i <= n; i++) 83 { 84 for (int j = i + 1; j <= n; j++) 85 { 86 int dis = 0; 87 for (int pos = 0; pos < 7; pos++) 88 { 89 if (mp[i][pos] != mp[j][pos]) dis++; 90 } 91 minHeap.push_back(side(i, j, dis)); 92 } 93 } 94 make_heap(minHeap.begin(), minHeap.end()); 95 int ans = Kruskal(); 96 printf("The highest possible quality is 1/%d. ", ans); 97 } 98 return 0; 99 }
7、POJ 2349 Arctic Network
题意:有S颗卫星和P个哨所,有卫星的两个哨所之间可以任意通信;否则,一个哨所只能和距离它小于等于D的哨所通信。给出卫星的数量和P个哨所的坐标,求D的最小值。
思路:用最小生成树求前P-S-1条最小边,D则为第P-S-1边。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 using namespace std; 7 int n,s; 8 const int maxn = 505; 9 int pre[maxn]; 10 char mp[maxn][10]; 11 struct side 12 { 13 int v1; 14 int v2; 15 double len; 16 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 17 { 18 } 19 friend bool operator <(const side&a, const side&b) 20 { 21 return a.len > b.len; 22 } 23 }; 24 vector<side>minHeap; 25 struct node 26 { 27 int x; 28 int y; 29 node(int xx=0,int yy=0):x(xx),y(yy){ } 30 }points[maxn]; 31 int Find(int x) 32 { 33 int r = x; 34 while (r != pre[r]) 35 { 36 r = pre[r]; 37 } 38 int c = x, p; 39 while (c != r) 40 { 41 p = pre[c]; 42 pre[c] = r; 43 c = p; 44 } 45 return r; 46 } 47 bool Join(int x, int y) 48 { 49 int f1 = Find(x), f2 = Find(y); 50 if (f1 != f2) 51 { 52 pre[f1] = f2; 53 return false; 54 } 55 else return true; 56 } 57 double Kruskal() 58 { 59 side tmp; 60 int cnt = s; 61 for (int i = 0; i <= n; i++) pre[i] = i; 62 double ans; 63 while (cnt < n) 64 { 65 pop_heap(minHeap.begin(), minHeap.end()); 66 tmp = minHeap.back(); 67 minHeap.pop_back(); 68 int u = Find(tmp.v1); 69 int v = Find(tmp.v2); 70 if (u != v) 71 { 72 Join(tmp.v1, tmp.v2); 73 ans = tmp.len; 74 cnt++; 75 } 76 } 77 return ans; 78 } 79 int main() 80 { 81 int t; 82 scanf("%d", &t); 83 while (t--) 84 { 85 scanf("%d%d",&s,&n); 86 minHeap.clear(); 87 for (int i = 1; i <= n; i++) 88 { 89 scanf("%d%d",&points[i].x,&points[i].y); 90 } 91 for (int i = 1; i <= n; i++) 92 { 93 for (int j = i + 1; j <= n; j++) 94 { 95 double dis = sqrt(1.0*(points[i].x-points[j].x)*(points[i].x - points[j].x)+ (points[i].y - points[j].y)*(points[i].y - points[j].y)); 96 minHeap.push_back(side(i, j, dis)); 97 } 98 } 99 make_heap(minHeap.begin(), minHeap.end()); 100 double re=Kruskal(); 101 printf("%.2lf ",re); 102 } 103 return 0; 104 }
8、poj 1751 Highways
题意:给你N个城市的坐标,城市之间存在公路,但是由于其中一些道路损坏了,需要维修,维修的费用与公路长成正比(公路是直的)。但现有M条公路是完整的,不需要维修,下面有M行,表示不需要维修的道路两端的城市,问最小费用。
思路:同4.
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 using namespace std; 7 int n; 8 const int maxn =760; 9 int pre[maxn]; 10 char mp[maxn][10]; 11 bool vis[maxn]; 12 struct side 13 { 14 int v1; 15 int v2; 16 double len; 17 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 18 { 19 } 20 friend bool operator <(const side&a, const side&b) 21 { 22 return a.len > b.len; 23 } 24 }; 25 vector<side>minHeap; 26 struct node 27 { 28 int x; 29 int y; 30 node(int xx = 0, int yy = 0) :x(xx), y(yy) 31 { 32 } 33 }points[maxn]; 34 int Find(int x) 35 { 36 int r = x; 37 while (r != pre[r]) 38 { 39 r = pre[r]; 40 } 41 int c = x, p; 42 while (c != r) 43 { 44 p = pre[c]; 45 pre[c] = r; 46 c = p; 47 } 48 return r; 49 } 50 bool Join(int x, int y) 51 { 52 int f1 = Find(x), f2 = Find(y); 53 if (f1 != f2) 54 { 55 pre[f1] = f2; 56 return false; 57 } 58 else return true; 59 } 60 double Kruskal(int cnt0) 61 { 62 side tmp; 63 double ans=0; 64 int cnt = cnt0; 65 66 while (cnt < n) 67 { 68 pop_heap(minHeap.begin(), minHeap.end()); 69 tmp = minHeap.back(); 70 minHeap.pop_back(); 71 int u = Find(tmp.v1); 72 int v = Find(tmp.v2); 73 if (u != v) 74 { 75 Join(tmp.v1, tmp.v2); 76 points[cnt - cnt0].x = tmp.v1, points[cnt - cnt0].y = tmp.v2; 77 cnt++; 78 } 79 } 80 return ans; 81 } 82 int main() 83 { 84 while (~scanf("%d",&n)) 85 { 86 minHeap.clear(); 87 for (int i = 1; i <= n; i++) 88 { 89 scanf("%d%d", &points[i].x, &points[i].y); 90 } 91 for (int i = 1; i <= n; i++) 92 { 93 for (int j = i + 1; j <= n; j++) 94 { 95 double dis = sqrt(1.0*(points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y)); 96 minHeap.push_back(side(i, j, dis)); 97 } 98 } 99 for (int i = 0; i <= n; i++) pre[i] = i; 100 int m; 101 scanf("%d", &m); 102 memset(vis, 0, sizeof(vis)); 103 int cnt = 1; 104 for (int i = 0; i < m; i++) 105 { 106 int u, v; 107 scanf("%d%d", &u, &v); 108 if (!Join(u, v)) cnt++; 109 } 110 make_heap(minHeap.begin(), minHeap.end()); 111 Kruskal(cnt); 112 for (int i = 0; i < n - cnt; i++) 113 { 114 printf("%d %d ", points[i].x, points[i].y); 115 } 116 } 117 return 0; 118 }
9、POJ 1258 Agri-Net
题意:有n个农场,已知这n个农场都互相相通,有一定的距离,现在每个农场需要装光纤,问怎么安装光纤能将所有农场都连通起来,并且要使光纤距离最小,输出安装光纤的总距离。
思路:最小生成树。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 using namespace std; 7 int n; 8 const int maxn = 760; 9 int pre[maxn]; 10 char mp[maxn][10]; 11 bool vis[maxn]; 12 struct side 13 { 14 int v1; 15 int v2; 16 int len; 17 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 18 { 19 } 20 friend bool operator <(const side&a, const side&b) 21 { 22 return a.len > b.len; 23 } 24 }; 25 vector<side>minHeap; 26 struct node 27 { 28 int x; 29 int y; 30 node(int xx = 0, int yy = 0) :x(xx), y(yy) 31 { 32 } 33 }points[maxn]; 34 int Find(int x) 35 { 36 int r = x; 37 while (r != pre[r]) 38 { 39 r = pre[r]; 40 } 41 int c = x, p; 42 while (c != r) 43 { 44 p = pre[c]; 45 pre[c] = r; 46 c = p; 47 } 48 return r; 49 } 50 bool Join(int x, int y) 51 { 52 int f1 = Find(x), f2 = Find(y); 53 if (f1 != f2) 54 { 55 pre[f1] = f2; 56 return false; 57 } 58 else return true; 59 } 60 int Kruskal(int cnt0) 61 { 62 side tmp; 63 int ans = 0; 64 int cnt = cnt0; 65 66 while (cnt < n) 67 { 68 pop_heap(minHeap.begin(), minHeap.end()); 69 tmp = minHeap.back(); 70 minHeap.pop_back(); 71 int u = Find(tmp.v1); 72 int v = Find(tmp.v2); 73 if (u != v) 74 { 75 Join(tmp.v1, tmp.v2); 76 ans += tmp.len; 77 cnt++; 78 } 79 } 80 return ans; 81 } 82 int main() 83 { 84 while (~scanf("%d", &n)) 85 { 86 minHeap.clear(); 87 for (int i = 1; i <= n; i++) 88 { 89 for (int j = 1; j <= n; j++) 90 { 91 int dis; 92 scanf("%d", &dis); 93 if (i == j)continue; 94 minHeap.push_back(side(i, j, dis)); 95 } 96 } 97 for (int i = 0; i <= n; i++) pre[i] = i; 98 int cnt = 1; 99 make_heap(minHeap.begin(), minHeap.end()); 100 int ans=Kruskal(cnt); 101 printf("%d ", ans); 102 } 103 return 0; 104 }
10、POJ 3026 Borg Maze
题意:在一个y行 x列的迷宫中,有可行走的通路空格’ ‘,不可行走的墙’#’,还有两种英文字母A和S,现在从S出发,要求用最短的路径L连接所有字母,输出这条路径L的总长度。
思路:先BFS求出所有两两之间的路径长度,然后建图求最小生成树。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 #include<queue> 7 #include<memory.h> 8 using namespace std; 9 int n,totalnum,rows,cols; 10 const int maxn = 210; 11 const int maxnum = 210; 12 int pre[maxnum]; 13 char mp[maxn][maxn]; 14 bool vis[maxn][maxn]; 15 int id[maxn][maxn]; 16 struct side 17 { 18 int v1; 19 int v2; 20 int len; 21 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 22 { 23 } 24 friend bool operator <(const side&a, const side&b) 25 { 26 return a.len > b.len; 27 } 28 }; 29 vector<side>minHeap; 30 struct node 31 { 32 int x; 33 int y; 34 int stps; 35 node(int xx = 0, int yy = 0,int stp=0) :x(xx), y(yy),stps(stp) 36 { 37 } 38 }; 39 int dx[] = { 0,0,1,-1 }; 40 int dy[] = { 1,-1,0,0 }; 41 int Find(int x) 42 { 43 int r = x; 44 while (r != pre[r]) 45 { 46 r = pre[r]; 47 } 48 int c = x, p; 49 while (c != r) 50 { 51 p = pre[c]; 52 pre[c] = r; 53 c = p; 54 } 55 return r; 56 } 57 bool Join(int x, int y) 58 { 59 int f1 = Find(x), f2 = Find(y); 60 if (f1 != f2) 61 { 62 pre[f1] = f2; 63 return false; 64 } 65 else return true; 66 } 67 void BFS(int rx,int ry) 68 { 69 memset(vis, 0, sizeof(vis)); 70 queue<node>q; 71 q.push(node(rx, ry, 0)); 72 vis[rx][ry] = true; 73 while (!q.empty()) 74 { 75 node u = q.front(); 76 q.pop(); 77 for (int i = 0; i < 4; i++) 78 { 79 int tx = u.x + dx[i]; 80 int ty = u.y + dy[i]; 81 if (tx >= 0 && tx < rows&&ty >= 0 && ty < cols) 82 { 83 if (mp[tx][ty] != '#' && !vis[tx][ty]) 84 { 85 q.push(node(tx, ty, u.stps + 1)); 86 vis[tx][ty] = true; 87 if (mp[tx][ty] == 'A' || mp[tx][ty] == 'S') 88 { 89 minHeap.push_back(side(id[rx][ry], id[tx][ty], u.stps + 1)); 90 } 91 } 92 } 93 } 94 } 95 } 96 int Kruskal(int cnt0) 97 { 98 side tmp; 99 int ans = 0; 100 int cnt = cnt0; 101 102 while (cnt < totalnum) 103 { 104 pop_heap(minHeap.begin(), minHeap.end()); 105 tmp = minHeap.back(); 106 minHeap.pop_back(); 107 int u = Find(tmp.v1); 108 int v = Find(tmp.v2); 109 if (u != v) 110 { 111 Join(tmp.v1, tmp.v2); 112 ans += tmp.len; 113 cnt++; 114 } 115 } 116 return ans; 117 } 118 int main() 119 { 120 int t; 121 scanf("%d", &t); 122 while (t--) 123 { 124 scanf("%d%d", &cols, &rows); 125 minHeap.clear(); 126 memset(id, 0, sizeof(id)); 127 int idorder = 1; 128 while ((cin.get()) == ' '); 129 for (int i = 0; i<rows; i++) 130 { 131 for (int j = 0; j <cols; j++) 132 { 133 scanf("%c",&mp[i][j]); 134 if (mp[i][j] == 'S' || mp[i][j] == 'A') 135 { 136 id[i][j] = idorder++; 137 } 138 } 139 cin.get(); 140 } 141 for (int i = 0; i < rows; i++) 142 { 143 for (int j = 0; j < cols; j++) 144 { 145 if (id[i][j]) 146 { 147 BFS(i, j); 148 } 149 } 150 } 151 for (int i = 0; i <= idorder; i++) pre[i] = i; 152 int cnt = 1; 153 totalnum = idorder - 1; 154 make_heap(minHeap.begin(), minHeap.end()); 155 int ans = Kruskal(cnt); 156 printf("%d ", ans); 157 } 158 return 0; 159 }
11、POJ 1679 The Unique MST
题意:给出n个点,m条边,判断其最小生成树是否唯一。
思路:先求出最小生成树,然后枚举删去最小生成树中的边,再求最小生成树,比较权值。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 #include<queue> 7 #include<memory.h> 8 using namespace std; 9 int n,m; 10 const int maxn = 200; 11 int pre[maxn]; 12 struct side 13 { 14 int v1; 15 int v2; 16 int len; 17 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 18 { 19 } 20 friend bool operator <(const side&a, const side&b) 21 { 22 return a.len > b.len; 23 } 24 }; 25 vector<side>minHeap; 26 vector<side>minheaptmp; 27 struct node 28 { 29 int from; 30 int to; 31 int len; 32 node(int ff=0,int tt = 0, int ll = 0) :from(ff),to(tt),len(ll) 33 { 34 } 35 }; 36 vector<node>mintree; 37 int Find(int x) 38 { 39 int r = x; 40 while (r != pre[r]) 41 { 42 r = pre[r]; 43 } 44 int c = x, p; 45 while (c != r) 46 { 47 p = pre[c]; 48 pre[c] = r; 49 c = p; 50 } 51 return r; 52 } 53 bool Join(int x, int y) 54 { 55 int f1 = Find(x), f2 = Find(y); 56 if (f1 != f2) 57 { 58 pre[f1] = f2; 59 return false; 60 } 61 else return true; 62 } 63 64 int Kruskal(int du,int dv,int flag) 65 { 66 side tmp; 67 int ans = 0; 68 int cnt = 1; 69 for (int i = 0; i <= n; i++)pre[i] = i; 70 while (cnt < n&&!minHeap.empty()) 71 { 72 pop_heap(minHeap.begin(), minHeap.end()); 73 tmp = minHeap.back(); 74 minHeap.pop_back(); 75 if ((tmp.v1 == du&&tmp.v2 == dv) || (tmp.v2 == du&&tmp.v1 == dv))continue; 76 int u = Find(tmp.v1); 77 int v = Find(tmp.v2); 78 if (u != v) 79 { 80 Join(tmp.v1, tmp.v2); 81 ans += tmp.len; 82 if (flag) 83 { 84 mintree.push_back(node(tmp.v1,tmp.v2, tmp.len)); 85 } 86 cnt++; 87 } 88 } 89 if (cnt < n&&minHeap.empty()) return -1; 90 else return ans; 91 } 92 int main() 93 { 94 int t; 95 scanf("%d", &t); 96 while (t--) 97 { 98 scanf("%d%d", &n,&m); 99 minHeap.clear(); 100 mintree.clear(); 101 for (int i = 0; i < m; i++) 102 { 103 int u, v, l; 104 scanf("%d%d%d", &u, &v, &l); 105 minHeap.push_back(side(u, v, l)); 106 } 107 minheaptmp = minHeap; 108 int inians = Kruskal(0, 0, 1); 109 bool flag = true; 110 for (int i = 0; i < n - 1; i++) 111 { 112 int u = mintree[i].from; 113 int v = mintree[i].to; 114 minHeap = minheaptmp; 115 int ans = Kruskal(u, v, 0); 116 if (ans == inians) 117 { 118 flag = false; 119 break; 120 } 121 } 122 if (flag)printf("%d ", inians); 123 else printf("Not Unique! "); 124 } 125 return 0; 126 }
12、HDU 1233 还是畅通工程
题意:某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
思路:最小生成树。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 int n; 6 const int maxn = 110; 7 int pre[maxn]; 8 struct side 9 { 10 int v1; 11 int v2; 12 int len; 13 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 14 { 15 } 16 friend bool operator <(const side&a, const side&b) 17 { 18 return a.len > b.len; 19 } 20 }; 21 vector<side>minHeap; 22 int Find(int x) 23 { 24 int r = x; 25 while (r != pre[r]) 26 { 27 r = pre[r]; 28 } 29 int c = x, p; 30 while (c != r) 31 { 32 p = pre[c]; 33 pre[c] = r; 34 c = p; 35 } 36 return r; 37 } 38 void Join(int x, int y) 39 { 40 int f1 = Find(x), f2 = Find(y); 41 if (f1 != f2) pre[f1] = f2; 42 } 43 int Kruskal() 44 { 45 side tmp; 46 int cnt = 1; 47 for (int i = 0; i <= n; i++)pre[i] = i; 48 int ans = 0; 49 while (cnt < n) 50 { 51 pop_heap(minHeap.begin(), minHeap.end()); 52 tmp = minHeap.back(); 53 minHeap.pop_back(); 54 int u = Find(tmp.v1); 55 int v = Find(tmp.v2); 56 if (u != v) 57 { 58 Join(tmp.v1, tmp.v2); 59 ans += tmp.len; 60 cnt++; 61 } 62 } 63 return ans; 64 } 65 int main() 66 { 67 while (~scanf("%d", &n)) 68 { 69 if (n == 0)break; 70 minHeap.clear(); 71 for (int i = 0; i < n*(n - 1)/2; i++) 72 { 73 int u, v, l; 74 scanf("%d%d%d", &u, &v, &l); 75 minHeap.push_back(side(u, v, l)); 76 } 77 make_heap(minHeap.begin(), minHeap.end()); 78 int ans = Kruskal(); 79 printf("%d ", ans); 80 } 81 return 0; 82 }
13、hdu 1875 畅通工程再续
题意:相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。
思路:最小生成树模板。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 int n; 6 const int maxn = 110; 7 int pre[maxn]; 8 struct side 9 { 10 int v1; 11 int v2; 12 double len; 13 side(int vv1 = 0, int vv2 = 0, double ll = 0) :v1(vv1), v2(vv2), len(ll) 14 { 15 } 16 friend bool operator <(const side&a, const side&b) 17 { 18 return a.len > b.len; 19 } 20 }; 21 vector<side>minHeap; 22 23 struct point 24 { 25 int x; 26 int y; 27 }points[maxn]; 28 int Find(int x) 29 { 30 int r = x; 31 while (r != pre[r]) 32 { 33 r = pre[r]; 34 } 35 int c = x, p; 36 while (c != r) 37 { 38 p = pre[c]; 39 pre[c] = r; 40 c = p; 41 } 42 return r; 43 } 44 void Join(int x, int y) 45 { 46 int f1 = Find(x), f2 = Find(y); 47 if (f1 != f2) pre[f1] = f2; 48 } 49 double Kruskal() 50 { 51 side tmp; 52 int cnt = 1; 53 for (int i = 0; i <= n; i++)pre[i] = i; 54 double ans = 0; 55 while (cnt < n&&!minHeap.empty()) 56 { 57 pop_heap(minHeap.begin(), minHeap.end()); 58 tmp = minHeap.back(); 59 minHeap.pop_back(); 60 int u = Find(tmp.v1); 61 int v = Find(tmp.v2); 62 if (u != v) 63 { 64 Join(tmp.v1, tmp.v2); 65 ans += tmp.len; 66 cnt++; 67 } 68 } 69 if (minHeap.empty() && cnt < n)return -1; 70 else return ans; 71 } 72 int main() 73 { 74 int t; 75 scanf("%d", &t); 76 while (t--) 77 { 78 scanf("%d", &n); 79 minHeap.clear(); 80 for (int i = 1; i <= n; i++) 81 { 82 scanf("%d%d",&points[i].x, &points[i].y); 83 } 84 for (int i = 1; i <= n; i++) 85 { 86 for (int j = i + 1; j <= n; j++) 87 { 88 double dis = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y)); 89 if (dis < 10 || dis>1000)continue; 90 minHeap.push_back(side(i, j, dis)); 91 } 92 } 93 make_heap(minHeap.begin(), minHeap.end()); 94 double ans = Kruskal(); 95 if(ans>0)printf("%.1lf ", ans*100); 96 else printf("oh! "); 97 } 98 return 0; 99 }
14、HDU 4081 Qin Shi Huang's National Road System(次小生成树模板)
题意:给定n个点的点权及相互间的边权,求生成树,现在能够使得其中一条边的边权变为0,求该0值边所连的两点的点权和/剩下的树边权的和的最大值。
思路:找到最小生成树,然后添加一条边构成环,再删掉环中属于最小树的最大边,用这种方法遍历所有边以找到最终ans。
1 //次小生成树可由最小生成树换一条边得到. 2 //题意: 3 //有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。 4 //秦始皇希望徐福能把要修的n - 1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。 5 //最终,秦始皇给出了一个公式,A / B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n - 2条路径长度之和,选使得A / B值最大的那条。 6 7 #include<iostream> 8 #include<algorithm> 9 using namespace std; 10 int n;//结点(城市)数目 11 const int maxn = 1010; 12 struct node 13 { 14 int x; 15 int y; 16 int v; 17 }points[maxn];//记录每个城市的坐标,以及城市的人口(结点值) 18 double dis[maxn][maxn];//根据点的坐标得到两点之间的距离 19 bool mintree[maxn][maxn];//标记最小生成树的边 20 double pathmax[maxn][maxn];//记录最小生成树中两点间路径的最大权值 21 int pre[maxn];//标记生成最小生成树中,每个结点的父结点 22 bool vis[maxn];//记录加入最小生成树的结点 23 24 void Init()//初始化:得到结点间的距离 25 { 26 for (int i = 1; i <= n; i++) 27 { 28 dis[i][i] = 0; 29 for (int j = i + 1; j <= n; j++) 30 { 31 double distance = sqrt((points[i].x - points[j].x)*(points[i].x - points[j].x) + (points[i].y - points[j].y)*(points[i].y - points[j].y)); 32 dis[i][j] = dis[j][i] = distance; 33 } 34 } 35 } 36 37 double Prime() 38 { 39 memset(vis, 0, sizeof(vis)); 40 memset(mintree, 0, sizeof(mintree)); 41 memset(pathmax, 0, sizeof(pathmax)); 42 double sum = 0; 43 double mincost[maxn];//记录当前权值 44 for (int i = 1; i <= n; i++) 45 { 46 mincost[i] = dis[1][i]; 47 pre[i] = 1; 48 } 49 vis[1] = true; 50 for (int i = 1; i < n; i++) 51 {//每次选1个点加入 52 int u = 0; 53 for (int j = 1; j <= n; j++) 54 {//从未加入的点找到一条最小的边 55 if (!vis[j]) 56 { 57 if (u == 0 || mincost[j] < mincost[u]) 58 { 59 u = j; 60 } 61 } 62 } 63 mintree[u][pre[u]] = mintree[pre[u]][u] = true; 64 sum += dis[u][pre[u]]; 65 vis[u] = true; 66 67 for (int j = 1; j <= n; j++) 68 { 69 if (vis[j] && j != u) 70 {//更新树中两点路径的最大权值 71 pathmax[u][j] = pathmax[j][u] = max(pathmax[j][pre[u]], dis[u][pre[u]]); 72 } 73 else if (!vis[j]) 74 {//更新已选结点到未选结点的最短距离 75 if (dis[u][j] < mincost[j]) 76 { 77 mincost[j] = dis[u][j]; 78 pre[j] = u; 79 } 80 } 81 } 82 } 83 return sum; 84 } 85 int main() 86 { 87 int t; 88 scanf("%d", &t); 89 while (t--)//t组测试数据 90 { 91 scanf("%d", &n); 92 for (int i = 1; i <= n; i++) 93 { 94 scanf("%d%d%d", &points[i].x, &points[i].y, &points[i].v); 95 } 96 Init(); 97 double inilen=Prime(); 98 double ratio = 0; 99 for (int i = 1; i <= n; i++) 100 { 101 for (int j = 1; j <= n; j++) 102 { 103 if (i == j) continue; 104 if (mintree[i][j])//如果选择的是最小生成树中的一条边,则剩下的路径长为inilen - dis[i][j] 105 { 106 ratio = max(ratio, (points[i].v + points[j].v) / (inilen - dis[i][j])); 107 } 108 else//如果选择的不是最小生成树中的边,那么加入该条边后就会形成一个环,需要删去最小生成树中i到j的路径中权值最大的边 109 { 110 ratio = max(ratio, (points[i].v + points[j].v) / (inilen - pathmax[i][j])); 111 } 112 } 113 } 114 printf("%.2lf ", ratio); 115 } 116 return 0; 117 }
15、UVA 10600 ACM Contest and Blackout
题意:给出一张图,问这张图的最小生成树的权值和次小生成树的权值,如果有两个最小生成树,则输出的权值都为最小生成树的权值。
思路:模板题。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 #include<queue> 7 #include<memory.h> 8 using namespace std; 9 int n, m; 10 const int maxn = 110; 11 const int maxm = 10010; 12 const int INF = 0x7fffffff; 13 int pre[maxn]; 14 struct side 15 { 16 int v1; 17 int v2; 18 int len; 19 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 20 { 21 } 22 friend bool operator <(const side&a, const side&b) 23 { 24 return a.len < b.len; 25 } 26 }; 27 vector<side>minHeap; 28 struct node 29 { 30 int to; 31 int len; 32 node(int tt = 0, int ll = 0) : to(tt), len(ll) 33 { 34 } 35 }; 36 vector<node>mintree[maxn]; 37 vector<int>mintreenodes; 38 int pathmax[maxn][maxn]; 39 void DFS(int cur, int fa,int dis) 40 { 41 int sz = mintreenodes.size(); 42 for (int i = 0; i < sz; i++) 43 { 44 int prefa = mintreenodes[i]; 45 pathmax[prefa][cur] = pathmax[cur][prefa] = max(pathmax[prefa][fa], dis); 46 } 47 mintreenodes.push_back(cur); 48 for (int i = 0; i < mintree[cur].size(); i++) 49 { 50 if (mintree[cur][i].to!=fa) 51 { 52 DFS(mintree[cur][i].to, cur, mintree[cur][i].len); 53 } 54 } 55 } 56 int Find(int x) 57 { 58 int r = x; 59 while (r != pre[r]) 60 { 61 r = pre[r]; 62 } 63 int c = x, p; 64 while (c != r) 65 { 66 p = pre[c]; 67 pre[c] = r; 68 c = p; 69 } 70 return r; 71 } 72 bool Join(int x, int y) 73 { 74 int f1 = Find(x), f2 = Find(y); 75 if (f1 != f2) 76 { 77 pre[f1] = f2; 78 return false; 79 } 80 else return true; 81 } 82 83 bool vis[maxm]; 84 int Kruskal() 85 { 86 side tmp; 87 int ans = 0; 88 for (int i = 0; i <= n; i++)pre[i] = i; 89 sort(minHeap.begin(), minHeap.end()); 90 for(int i=0;i<m;i++) 91 { 92 tmp = minHeap[i]; 93 int u = Find(tmp.v1); 94 int v = Find(tmp.v2); 95 if (u != v) 96 { 97 Join(tmp.v1, tmp.v2); 98 ans += tmp.len; 99 mintree[tmp.v1].push_back(node(tmp.v2, tmp.len)); 100 mintree[tmp.v2].push_back(node(tmp.v1, tmp.len)); 101 vis[i] = true; 102 } 103 } 104 return ans; 105 } 106 int main() 107 { 108 int t; 109 scanf("%d", &t); 110 while (t--) 111 { 112 scanf("%d%d", &n, &m); 113 114 minHeap.clear(); 115 mintreenodes.clear(); 116 memset(pathmax, 0, sizeof(pathmax)); 117 for (int i = 0; i <= n;i++)mintree[i].clear(); 118 119 120 for (int i = 0; i < m; i++) 121 { 122 int u, v, l; 123 scanf("%d%d%d", &u, &v, &l); 124 minHeap.push_back(side(u, v, l)); 125 } 126 memset(vis, 0, sizeof(vis)); 127 int inians = Kruskal(); 128 DFS(1, 0, 0); 129 int len2 = INF; 130 for (int i = 0; i < m; i++) 131 { 132 if (!vis[i]) 133 { 134 len2 = min(len2, inians + minHeap[i].len - pathmax[minHeap[i].v1][minHeap[i].v2]); 135 } 136 } 137 printf("%d %d ", inians, len2); 138 } 139 return 0; 140 }
16、UVA 10462 Is There A Second Way Left?
题意:给出无向图,问能否有一最小生成树,若有,问能否有一不同的次小生成树。
思路:次小生成树模板。
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<cmath> 5 #include<set> 6 #include<queue> 7 #include<memory.h> 8 using namespace std; 9 int n, m; 10 const int maxn = 110; 11 const int maxm = 10010; 12 const int INF = 0x7fffffff; 13 int pre[maxn]; 14 struct side 15 { 16 int v1; 17 int v2; 18 int len; 19 side(int vv1 = 0, int vv2 = 0, int ll = 0) :v1(vv1), v2(vv2), len(ll) 20 { 21 } 22 friend bool operator <(const side&a, const side&b) 23 { 24 return a.len < b.len; 25 } 26 }; 27 vector<side>minHeap; 28 struct node 29 { 30 int to; 31 int len; 32 node(int tt = 0, int ll = 0) : to(tt), len(ll) 33 { 34 } 35 }; 36 vector<node>mintree[maxn]; 37 vector<int>mintreenodes; 38 int pathmax[maxn][maxn]; 39 void DFS(int cur, int fa, int dis) 40 { 41 int sz = mintreenodes.size(); 42 for (int i = 0; i < sz; i++) 43 { 44 int prefa = mintreenodes[i]; 45 pathmax[prefa][cur] = pathmax[cur][prefa] = max(pathmax[prefa][fa], dis); 46 } 47 mintreenodes.push_back(cur); 48 for (int i = 0; i < mintree[cur].size(); i++) 49 { 50 if (mintree[cur][i].to != fa) 51 { 52 DFS(mintree[cur][i].to, cur, mintree[cur][i].len); 53 } 54 } 55 } 56 int Find(int x) 57 { 58 int r = x; 59 while (r != pre[r]) 60 { 61 r = pre[r]; 62 } 63 int c = x, p; 64 while (c != r) 65 { 66 p = pre[c]; 67 pre[c] = r; 68 c = p; 69 } 70 return r; 71 } 72 bool Join(int x, int y) 73 { 74 int f1 = Find(x), f2 = Find(y); 75 if (f1 != f2) 76 { 77 pre[f1] = f2; 78 return false; 79 } 80 else return true; 81 } 82 83 bool vis[maxm]; 84 int Kruskal() 85 { 86 side tmp; 87 int ans = 0; 88 for (int i = 0; i <= n; i++)pre[i] = i; 89 sort(minHeap.begin(), minHeap.end()); 90 int cnt = 1; 91 for (int i = 0; i<m; i++) 92 { 93 tmp = minHeap[i]; 94 int u = Find(tmp.v1); 95 int v = Find(tmp.v2); 96 if (u != v) 97 { 98 Join(tmp.v1, tmp.v2); 99 ans += tmp.len; 100 mintree[tmp.v1].push_back(node(tmp.v2, tmp.len)); 101 mintree[tmp.v2].push_back(node(tmp.v1, tmp.len)); 102 vis[i] = true; 103 cnt++; 104 } 105 } 106 if (cnt < n)return -1; 107 else return ans; 108 } 109 int main() 110 { 111 int t; 112 scanf("%d", &t); 113 int Case = 1; 114 while (t--) 115 { 116 scanf("%d%d", &n, &m); 117 118 minHeap.clear(); 119 mintreenodes.clear(); 120 memset(pathmax, 0, sizeof(pathmax)); 121 for (int i = 0; i <= n; i++)mintree[i].clear(); 122 123 124 for (int i = 0; i < m; i++) 125 { 126 int u, v, l; 127 scanf("%d%d%d", &u, &v, &l); 128 minHeap.push_back(side(u, v, l)); 129 } 130 memset(vis, 0, sizeof(vis)); 131 int inians = Kruskal(); 132 if (inians == -1) 133 { 134 printf("Case #%d : No way ", Case++); 135 continue; 136 } 137 DFS(1, 0, 0); 138 int len2 = INF; 139 for (int i = 0; i < m; i++) 140 { 141 if (!vis[i]) 142 { 143 len2 = min(len2, inians + minHeap[i].len - pathmax[minHeap[i].v1][minHeap[i].v2]); 144 } 145 } 146 if (len2!=INF) 147 { 148 printf("Case #%d : %d ", Case++, len2); 149 } 150 else printf("Case #%d : No second way ", Case++); 151 } 152 return 0; 153 }
17、poj 3164 Command Network
题意:给出n个点的坐标,给出m条有向边,问以1为根的最小树形图的权值。
思路:最小树形图模板。用邻接矩阵表示,适合点小于1000的情况
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <algorithm> 7 #include <string> 8 typedef long long LL; 9 using namespace std; 10 const int maxv = 110; 11 const int maxe = 10100; 12 const int INF = 0x3f3f3f3f; 13 int n, m; 14 double mp[maxv][maxv]; 15 //求具有V个点,以root为根节点的图mp的最小树形图 16 double zhuliu(int root, int V) 17 {//mp[][]存[i][j]有向边的权,目前没有把缩点展开 18 //如果不存在最小树形图,返回-1,否则返回最小树形图的权值 19 bool visited[maxv]; 20 bool flag[maxv];//缩点标记为true,否则仍然存在 21 int pre[maxv];//点i的父节点为pre[i] 22 double sum = 0;//最小树形图的权值 23 int i, j, k; 24 for (i = 0; i <= V; i++) flag[i] = false, mp[i][i] = 1.0*INF; 25 pre[root] = root; 26 while (true) 27 { 28 for (i = 1; i <= V; i++) 29 {//求最短弧集合E0 30 if (flag[i] || i == root) continue;//跳过根和缩点 31 pre[i] = i; 32 for (j = 1; j <= V; j++)//找最小的入边 33 if (!flag[j] && mp[j][i] < mp[pre[i]][i]) 34 pre[i] = j; 35 if (pre[i] == i) return -1;//除了跟以外有点没有入边,则根无法到达它 36 } 37 for (i = 1; i <= V; i++) 38 {//检查E0 39 if (flag[i] || i == root) continue; 40 for (j = 1; j <= V; j++) visited[j] = false; 41 visited[root] = true; 42 j = i;//从当前点开始找环 43 do 44 { 45 visited[j] = true; 46 j = pre[j]; 47 } while (!visited[j]); 48 if (j == root)continue;//没找到环 49 i = j;//收缩G中的有向环 50 do 51 {//将整个环的取值保存,累计计入原图的最小树形图 52 sum += mp[pre[j]][j]; 53 j = pre[j]; 54 } while (j != i); 55 j = i; 56 do 57 {//对于环上的点有关的边,修改其权值 58 for (k = 1; k <= V; k++) 59 if (!flag[k] && mp[k][j] < INF && k != pre[j]) 60 mp[k][j] -= mp[pre[j]][j]; 61 j = pre[j]; 62 } while (j != i); 63 for (j = 1; j <= V; j++) 64 {//缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i 65 if (j == i) continue; 66 for (k = pre[i]; k != i; k = pre[k]) 67 { 68 if (mp[k][j] < mp[i][j]) mp[i][j] = mp[k][j]; 69 if (mp[j][k] < mp[j][i]) mp[j][i] = mp[j][k]; 70 } 71 } 72 for (j = pre[i]; j != i; j = pre[j]) flag[j] = true;//标记环上其他点为被缩掉 73 break;//当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图 74 } 75 if (i > V) 76 {//如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是最小树形图.累计计入sum,算法结束 77 for (i = 1; i <= V; i++) 78 if (!flag[i] && i != root) sum += mp[pre[i]][i]; 79 break; 80 } 81 } 82 return sum; 83 } 84 struct node 85 { 86 int x; 87 int y; 88 }nodes[maxv]; 89 int main() 90 { 91 while (~scanf("%d%d", &n, &m)) 92 { 93 for (int i = 1; i <= n; i++) scanf("%d%d", &nodes[i].x, &nodes[i].y); 94 for (int i = 1; i <= n; i++) 95 { 96 for (int j = 1; j <= n; j++) mp[i][j] = 1.0*INF; 97 } 98 for (int i = 1; i <= m; i++) 99 { 100 int u, v; 101 scanf("%d%d", &u, &v); 102 double dis = sqrt(1.0*(nodes[u].x - nodes[v].x)*(nodes[u].x - nodes[v].x) + (nodes[u].y - nodes[v].y)*(nodes[u].y - nodes[v].y)); 103 mp[u][v] = dis; 104 } 105 double ans = zhuliu(1, n); 106 if (ans == -1)printf("poor snoopy "); 107 else printf("%.2lf ", ans); 108 } 109 return 0; 110 }
18、UVA 11183 Teen Girl Squad
题意:给出n个女孩,给出A打给B的电话费用,现在1号女孩要把信息传给其他所有人,问最少花费。
思路:最小树形图。注意输入时编号从0开始。
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <algorithm> 7 #include <string> 8 typedef long long LL; 9 using namespace std; 10 const int maxv = 1010; 11 const int maxe = 40100; 12 const int INF = 0x3f3f3f3f; 13 int n, m; 14 int mp[maxv][maxv]; 15 //求具有V个点,以root为根节点的图mp的最小树形图 16 int zhuliu(int root, int V) 17 {//mp[][]存[i][j]有向边的权,目前没有把缩点展开 18 //如果不存在最小树形图,返回-1,否则返回最小树形图的权值 19 //结点编号从1开始 20 bool visited[maxv]; 21 bool flag[maxv];//缩点标记为true,否则仍然存在 22 int pre[maxv];//点i的父节点为pre[i] 23 int sum = 0;//最小树形图的权值 24 int i, j, k; 25 for (i = 0; i <= V; i++) flag[i] = false, mp[i][i] = INF; 26 pre[root] = root; 27 while (true) 28 { 29 for (i = 1; i <= V; i++) 30 {//求最短弧集合E0 31 if (flag[i] || i == root) continue;//跳过根和缩点 32 pre[i] = i; 33 for (j = 1; j <= V; j++)//找最小的入边 34 if (!flag[j] && mp[j][i] < mp[pre[i]][i]) 35 pre[i] = j; 36 if (pre[i] == i) return -1;//除了跟以外有点没有入边,则根无法到达它 37 } 38 for (i = 1; i <= V; i++) 39 {//检查E0 40 if (flag[i] || i == root) continue; 41 for (j = 1; j <= V; j++) visited[j] = false; 42 visited[root] = true; 43 j = i;//从当前点开始找环 44 do 45 { 46 visited[j] = true; 47 j = pre[j]; 48 } while (!visited[j]); 49 if (j == root)continue;//没找到环 50 i = j;//收缩G中的有向环 51 do 52 {//将整个环的取值保存,累计计入原图的最小树形图 53 sum += mp[pre[j]][j]; 54 j = pre[j]; 55 } while (j != i); 56 j = i; 57 do 58 {//对于环上的点有关的边,修改其权值 59 for (k = 1; k <= V; k++) 60 if (!flag[k] && mp[k][j] < INF && k != pre[j]) 61 mp[k][j] -= mp[pre[j]][j]; 62 j = pre[j]; 63 } while (j != i); 64 for (j = 1; j <= V; j++) 65 {//缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i 66 if (j == i) continue; 67 for (k = pre[i]; k != i; k = pre[k]) 68 { 69 if (mp[k][j] < mp[i][j]) mp[i][j] = mp[k][j]; 70 if (mp[j][k] < mp[j][i]) mp[j][i] = mp[j][k]; 71 } 72 } 73 for (j = pre[i]; j != i; j = pre[j]) flag[j] = true;//标记环上其他点为被缩掉 74 break;//当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图 75 } 76 if (i > V) 77 {//如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是最小树形图.累计计入sum,算法结束 78 for (i = 1; i <= V; i++) 79 if (!flag[i] && i != root) sum += mp[pre[i]][i]; 80 break; 81 } 82 } 83 return sum; 84 } 85 struct node 86 { 87 int x; 88 int y; 89 }nodes[maxv]; 90 int main() 91 { 92 int t; 93 scanf("%d", &t); 94 int Case = 1; 95 while (t--) 96 { 97 scanf("%d%d", &n, &m); 98 for (int i = 1; i <= n; i++) 99 { 100 for (int j = 1; j <= n; j++) mp[i][j] = INF; 101 } 102 for (int i = 1; i <= m; i++) 103 { 104 int u, v, w; 105 scanf("%d%d%d", &u, &v, &w); 106 u++, v++; 107 mp[u][v] = w; 108 } 109 int ans = zhuliu(1, n); 110 if (ans == -1)printf("Case #%d: Possums! ", Case++); 111 else printf("Case #%d: %d ", Case++, ans); 112 } 113 return 0; 114 }
19、hdu 2121 Ice_cream’s world II
题意:有n个城市和m条有向边,现在要让你选一处为城堡,使其到其他城市的路径长度最小。
思路:无根最小树形图。建立超级源点,和其他点建立一条有向边,边权为原本所有有向边的权值之和+1.最后,最小树形图的根为和超级源点相连的点。由于城市数目过多,不能再用邻接矩阵,用边表存。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 #define N 1010 6 #define INF 0x7f7f7f7f 7 struct Edge 8 { 9 int u, v, w; 10 } e[N*N]; 11 int cnt; 12 int in[N]; 13 int vis[N], pre[N], id[N]; 14 int minroot; 15 void addedge(int u, int v, int w) 16 { 17 e[cnt].u = u; 18 e[cnt].v = v; 19 e[cnt++].w = w; 20 } 21 int Directed_MST(int root, int NV, int NE) 22 {//root根结点,nv为结点数目,ne为边数 23 int ret = 0;//存储最小树形图的边权 24 while (true) 25 { 26 //步骤1:找到最小边 27 for (int i = 0; i < NV; i++) 28 in[i] = INF; 29 memset(pre, -1, sizeof(pre)); 30 for (int i = 0; i < NE; i++) 31 { 32 int u = e[i].u, v = e[i].v; 33 if (e[i].w < in[v] && u != v&&v!=root) 34 { 35 pre[v] = u; 36 in[v] = e[i].w; 37 if (u == root) minroot = i;//存的是边的编号 38 } 39 } 40 for (int i = 0; i < NV; i++) 41 { 42 if (i == root) continue; 43 if (in[i] == INF) return -1;//除了根节点以外有点没有入边,则根无法到达他 44 } 45 int cntnode = 0; 46 memset(id, -1, sizeof(id)); 47 memset(vis, -1, sizeof(vis)); 48 //找环 49 in[root] = 0; 50 for (int i = 0; i < NV; i++) //标记每个环,编号 51 { 52 ret += in[i]; 53 int v = i; 54 while (vis[v] != i && id[v] == -1 && v != root) 55 { 56 vis[v] = i; 57 v = pre[v]; 58 } 59 if (v != root && id[v] == -1)//成环 60 { 61 for (int u = pre[v]; u != v; u = pre[u]) 62 {//编号 63 id[u] = cntnode; 64 } 65 id[v] = cntnode++; 66 } 67 } 68 if (cntnode == 0) break;//无环 69 for (int i = 0; i < NV; i++) 70 if (id[i] == -1) 71 id[i] = cntnode++; 72 //步骤3:缩点,重新标记 73 for (int i = 0; i < NE; i++) 74 { 75 int u = e[i].u; 76 int v = e[i].v; 77 e[i].u = id[u]; 78 e[i].v = id[v]; 79 if (e[i].u != e[i].v) e[i].w -= in[v]; 80 } 81 NV = cntnode; 82 root = id[root]; 83 } 84 return ret;//最小树形图的权值 85 } 86 87 int main() 88 { 89 int n, m, sum; 90 int u, v, w; 91 while (~scanf("%d%d", &n, &m)) 92 { 93 cnt = 0; sum = 0; 94 for (int i = 0; i<m; i++) 95 { 96 scanf("%d %d %d", &u, &v, &w); 97 addedge(u + 1, v + 1, w); 98 sum += w; 99 } 100 sum++; 101 for (int i = 1; i <= n; i++) 102 addedge(0, i, sum); 103 int ans = Directed_MST(0, n + 1, cnt); 104 if (ans == -1 || ans >= 2 * sum) 105 printf("impossible "); 106 else 107 printf("%d %d ", ans - sum, minroot - m);//第m+i条边和超级源点相连 108 } 109 return 0; 110 }
20、hdu 4009 Transfer water
题意:有N个点,每个点都有相应的三维坐标(x,y,z),现在要求每个点都能获得水,或者水的方式有两种
1.自己挖井,费用为X * 海拔高度z
2.铺设管道引水(只能从指定的点引水):a.如果海拔高度小于引水处,费用为两地曼哈顿距离*Y ;b.如果海拔高度大于饮水处,费用为两地曼哈顿距离*Y + Z
求最小花费。
思路:设置一个虚根,虚根引向所有的点,权值为挖井的费用,接着按照要求连边,求出最小树形图即可。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 #define N 1010 6 #define INF 0x7f7f7f7f 7 struct Edge 8 { 9 int u, v, w; 10 } e[N*N]; 11 int cnt; 12 int in[N]; 13 int vis[N], pre[N], id[N]; 14 int minroot; 15 void addedge(int u, int v, int w) 16 { 17 e[cnt].u = u; 18 e[cnt].v = v; 19 e[cnt++].w = w; 20 } 21 int Directed_MST(int root, int NV, int NE) 22 {//root根结点,nv为结点数目,ne为边数 23 int ret = 0;//存储最小树形图的边权 24 while (true) 25 { 26 //步骤1:找到最小边 27 for (int i = 0; i < NV; i++) 28 in[i] = INF; 29 memset(pre, -1, sizeof(pre)); 30 for (int i = 0; i < NE; i++) 31 { 32 int u = e[i].u, v = e[i].v; 33 if (e[i].w < in[v] && u != v&&v != root) 34 { 35 pre[v] = u; 36 in[v] = e[i].w; 37 if (u == root) minroot = i;//存的是边的编号 38 } 39 } 40 for (int i = 0; i < NV; i++) 41 { 42 if (i == root) continue; 43 if (in[i] == INF) return -1;//除了根节点以外有点没有入边,则根无法到达他 44 } 45 int cntnode = 0; 46 memset(id, -1, sizeof(id)); 47 memset(vis, -1, sizeof(vis)); 48 //找环 49 in[root] = 0; 50 for (int i = 0; i < NV; i++) //标记每个环,编号 51 { 52 ret += in[i]; 53 int v = i; 54 while (vis[v] != i && id[v] == -1 && v != root) 55 { 56 vis[v] = i; 57 v = pre[v]; 58 } 59 if (v != root && id[v] == -1)//成环 60 { 61 for (int u = pre[v]; u != v; u = pre[u]) 62 {//编号 63 id[u] = cntnode; 64 } 65 id[v] = cntnode++; 66 } 67 } 68 if (cntnode == 0) break;//无环 69 for (int i = 0; i < NV; i++) 70 if (id[i] == -1) 71 id[i] = cntnode++; 72 //步骤3:缩点,重新标记 73 for (int i = 0; i < NE; i++) 74 { 75 int u = e[i].u; 76 int v = e[i].v; 77 e[i].u = id[u]; 78 e[i].v = id[v]; 79 if (e[i].u != e[i].v) e[i].w -= in[v]; 80 } 81 NV = cntnode; 82 root = id[root]; 83 } 84 return ret;//最小树形图的权值 85 } 86 struct house 87 { 88 int x; 89 int y; 90 int h; 91 }hh[N]; 92 int main() 93 { 94 int n, m, sum,X,Y,Z; 95 int u, v, w; 96 while (~scanf("%d%d%d%d", &n, &X,&Y,&Z)) 97 { 98 if (n == 0 && X == 0 && Y == 0 && Z == 0)break; 99 cnt = 0; 100 for (int i = 1; i<=n; i++) 101 { 102 scanf("%d%d%d", &hh[i].x, &hh[i].y, &hh[i].h); 103 } 104 for (int i = 1; i <= n; i++) 105 { 106 int num; 107 scanf("%d", &num); 108 for (int j = 0; j < num; j++) 109 { 110 int v; 111 scanf("%d", &v); 112 if (v != i) 113 { 114 int cost; 115 if (hh[v].h <= hh[i].h)cost = (abs(hh[v].x - hh[i].x) + abs(hh[v].y - hh[i].y)+abs(hh[v].h-hh[i].h))*Y; 116 else cost = (abs(hh[v].x - hh[i].x) + abs(hh[v].y - hh[i].y)+abs(hh[v].h - hh[i].h))*Y + Z; 117 addedge(i, v, cost); 118 } 119 } 120 } 121 for (int i = 1; i <= n; i++) 122 addedge(0, i, hh[i].h*X); 123 int ans = Directed_MST(0, n + 1, cnt); 124 if (ans == -1) 125 printf("poor XiaoA "); 126 else 127 printf("%d ", ans); 128 } 129 return 0; 130 }
21、UVA 10766 Organising the Organisation(无向图生成树计数--Matrix-Tree定理)
题意:给出n, m, k。表示n个点,其中m条边不能直接连通,求生成树个数。
思路:生成树计数:Matrix-Tree定理模板。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N = 55; 8 typedef long long LL; 9 LL C[N][N]; 10 bool can[N][N]; 11 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。 12 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。 13 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理 14 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连) 15 LL ret = 1; 16 for (int i = 1; i<n; i++) 17 { 18 for (int j = i + 1; j<n; j++) 19 while (c[j][i]) 20 { 21 LL t = c[i][i] / c[j][i]; 22 for (int k = i; k<n; k++) 23 c[i][k] = (c[i][k] - c[j][k] * t); 24 for (int k = i; k<n; k++) 25 swap(c[i][k], c[j][k]); 26 ret = -ret; 27 } 28 if (c[i][i] == 0) 29 return 0; 30 ret = ret*c[i][i]; 31 } 32 if (ret<0) 33 ret = -ret; 34 return ret; 35 } 36 37 int main() 38 { 39 int n, m, k; 40 while (~scanf("%d%d%d", &n, &m,&k)) 41 { 42 memset(C, 0, sizeof(C)); 43 memset(can, true, sizeof(can)); 44 int u, v; 45 while (m--) 46 { 47 scanf("%d%d", &u, &v); 48 u--; 49 v--; 50 can[u][v] = can[v][u] = false; 51 } 52 for (int i = 0; i < n; i++) 53 { 54 for (int j = i+1; j < n; j++) 55 { 56 if (can[i][j]) 57 { 58 C[i][i]++, C[j][j]++; 59 C[i][j] = -1, C[j][i] = -1; 60 } 61 } 62 } 63 printf("%lld ", det(C, n)); 64 } 65 return 0; 66 }
22、URAL 1627 Join
题意:给出一张图,'.'表示卧室,‘*’表示茶水间。每个房间四周都有墙,现在要把相邻的卧室的墙打通,求有多少种方案使得两两之间只有一条通路。
思路:给所有卧室编号,相邻的建边,求生成树个数。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N = 110; 8 const int maxn = 15; 9 typedef long long LL; 10 LL C[N][N]; 11 int mp[maxn][maxn]; 12 char s[maxn]; 13 int dr[] = { 0,0,1,-1 }; 14 int dc[] = { 1,-1,0,0 }; 15 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。 16 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。 17 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理 18 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连) 19 LL ret = 1; 20 for (int i = 1; i<n; i++) 21 { 22 for (int j = i + 1; j<n; j++) 23 while (c[j][i]) 24 { 25 LL t = c[i][i] / c[j][i]; 26 for (int k = i; k<n; k++) 27 c[i][k] = (c[i][k] - c[j][k] * t); 28 for (int k = i; k<n; k++) 29 swap(c[i][k], c[j][k]); 30 ret = -ret; 31 } 32 if (c[i][i] == 0) 33 return 0; 34 ret = ret*c[i][i]; 35 } 36 if (ret<0) 37 ret = -ret; 38 return ret; 39 } 40 41 int main() 42 { 43 int n, m, k; 44 while (~scanf("%d%d", &n, &m)) 45 { 46 memset(C, 0, sizeof(C)); 47 memset(mp, -1, sizeof(mp)); 48 int count = 0; 49 for(int i=0;i<n;i++) 50 { 51 scanf("%s", s); 52 for (int j = 0; j < m; j++) 53 { 54 if (s[j] == '.') mp[i][j] = count++; 55 } 56 } 57 for (int i = 0; i < n; i++) 58 { 59 for (int j = 0; j < m; j++) 60 { 61 if (mp[i][j]>-1) 62 { 63 for (int k = 0; k < 4; k++) 64 { 65 int di = i + dr[k]; 66 int dj = j + dc[k]; 67 if (di >= 0 && di < n&&dj >= 0 && dj<m&&mp[di][dj]>-1) 68 { 69 int u = mp[i][j], v = mp[di][dj]; 70 C[u][u]++; 71 C[u][v] = -1; 72 } 73 } 74 } 75 } 76 } 77 printf("%lld ", det(C, count)); 78 } 79 return 0; 80 }
22、HDU 4305 Lightning
题意:闪电第一次会击中一个机器人然后会扩散到距离其不超过r并且之间无其他机器人的机器人的身上,然后继续扩散。问有多少种方案可以击中所有机器人。
思路:按照要求建边,然后跑生成树计数模板。参考:http://www.cnblogs.com/flipped/p/5767113.html
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N = 310; 8 const int MOD=10007; 9 typedef long long LL; 10 LL C[N][N]; 11 bool can[N][N]; 12 13 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。 14 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。 15 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理 16 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连) 17 LL ret = 1; 18 for (int i = 0; i < n; i++) 19 { 20 for (int j = 0; j < n; j++) c[i][j] %=MOD; 21 } 22 for (int i = 1; i<n; i++) 23 { 24 for (int j = i + 1; j<n; j++) 25 while (c[j][i]) 26 { 27 LL t = c[i][i] / c[j][i];//不用逆元,类似辗转相除 28 for (int k = i; k<n; k++) 29 c[i][k] = (c[i][k] - c[j][k] * t)%MOD; 30 for (int k = i; k<n; k++) 31 swap(c[i][k], c[j][k]); 32 ret = -ret; 33 } 34 if (c[i][i] == 0) 35 return 0; 36 ret = ret*c[i][i]%MOD; 37 } 38 if (ret < 0) 39 return ret = (MOD + ret) % MOD; 40 return ret%MOD; 41 } 42 43 struct point 44 { 45 int x; 46 int y; 47 }pp[N]; 48 bool isok(const point&a, const point&b, const point&c,int r) 49 {//判断点a和点b是否距离小于要求值r,并且中间连线没有第三点c 50 if ((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y) > r*r)return false; 51 else 52 { 53 if ((c.y - a.y)*(b.x - a.x) == (b.y - a.y)*(c.x - a.x) && c.x <= max(a.x, b.x) && c.x >= min(a.x, b.x)) return false; 54 else return true; 55 } 56 } 57 int main() 58 { 59 int t; 60 int n,r; 61 scanf("%d", &t); 62 while (t--) 63 { 64 scanf("%d%d", &n, &r); 65 memset(C, 0, sizeof(C)); 66 memset(can, false, sizeof(can)); 67 int u, v; 68 for(int i=0;i<n;i++) 69 { 70 scanf("%d%d", &pp[i].x, &pp[i].y); 71 } 72 for (int i = 0; i < n; i++) 73 { 74 for (int j = i + 1; j < n; j++) 75 { 76 bool iscan = true; 77 for (int k = 0; k < n; k++) 78 { 79 if (k!=i&&k!=j&&!isok(pp[i], pp[j], pp[k], r)) 80 { 81 iscan = false; 82 break; 83 } 84 } 85 if (iscan) 86 { 87 C[i][i]++, C[j][j]++; 88 C[i][j] = C[j][i] = -1; 89 } 90 } 91 } 92 int ans = det(C, n); 93 if (ans == 0) printf("-1 "); 94 else printf("%d ", ans); 95 } 96 return 0; 97 }
23、HDU 4408 Minimum Spanning Tree
题意:给出带权无向图,求最小生成树的个数。
思路:http://blog.csdn.net/Ramay7/article/details/51899421
http://www.cnblogs.com/jcf94/p/4071098.html
Kruskal+Matrix-Tree定理。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 typedef long long ll; 9 const int N = 105; //点的个数,点标从1-n 10 const int M = 1005; //边的个数 11 int n, m, u, v, d; 12 ll MOD; 13 struct UF 14 {//并查集 15 int pre[N]; 16 void init(int n) 17 { 18 for (int i = 0; i <= n; i++) pre[i] = i; 19 } 20 int Find(int x) 21 { 22 if (pre[x] == x)return x; 23 else 24 { 25 int fa = pre[x]; 26 pre[x] = Find(fa); 27 return pre[x]; 28 } 29 } 30 int Union(int x, int y) 31 { 32 int xx = Find(x); 33 int yy = Find(y); 34 if (xx == yy) return -1; 35 pre[xx] = yy; 36 return 1; 37 } 38 }a, b;//a,b都是并查集,b为每组边临时使用 39 40 struct eg 41 { 42 int u, v, w; 43 friend bool operator<(const eg &a, const eg&b) 44 { 45 return a.w < b.w; 46 } 47 }edge[M]; 48 int edgenum; 49 void add(int u, int v, int d) 50 {//边从1开始编号 51 edge[++edgenum].u = u, edge[edgenum].v = v, edge[edgenum].w = d; 52 } 53 54 bool visit[N]; 55 vector<int> gra[N]; 56 ll C[N][N], deg[N][N];//deg为邻接矩阵,deg[i][j]是点vi和vj之间的边数;C为Kirchhoff矩阵 57 58 ll DET(ll a[][N], int n, ll MOD) 59 { 60 int i, j, k; 61 ll temp = 1, t; 62 for (i = 0; i < n; i++) for (j = 0; j < n; j++) a[i][j] %= MOD; 63 for (i = 1; i < n; i++) 64 { 65 for (j = i + 1; j < n; j++) while (a[j][i]) 66 { 67 t = a[i][i] / a[j][i]; 68 for (k = i; k < n; k++) 69 { 70 a[i][k] -= a[j][k] * t; 71 a[i][k] %= MOD; 72 } 73 for (k = i; k < n; k++) 74 swap(a[i][k], a[j][k]); 75 76 temp = -temp; 77 } 78 temp = temp*a[i][i] % MOD; 79 } 80 return (temp + MOD) % MOD; 81 } 82 //假设存在n1条长度为c1的边,n2条长度为c2的边...则Kruskal首先处理c1边的连通性,然后处理c2边的连通性,对于c1边的连通性的处理可能有多种方案,即从n1条边中取出一定数量的边构成最大连通图,但是最终处理完之后的结果对于c2来说是完全一样的。因此算法就出来了,在Kruskal的基础上,使用Matrix-Tree定理处理每个阶段生成树的种数,最后将所有阶段的结果相乘即可。 83 //在Kruskal的基础上,每完成一个阶段(检查完一个长度),就将所有遍历过的点缩成一个点,然后用Matrix - Tree定理计算该点与下一组点组成的连通图中生成树的个数。最终把每一个阶段的结果相乘即可。 84 ll cal_MST_count(int n, ll MOD) 85 { 86 sort(edge + 1, edge + edgenum + 1); 87 int prew = edge[1].w; 88 ll ans = 1; 89 a.init(n); 90 b.init(n); 91 memset(visit, 0, sizeof(visit)); 92 memset(deg, 0, sizeof(deg)); 93 for (int i = 0; i <= n; i++) gra[i].clear(); 94 for (int t = 1; t <= edgenum + 1; t++) 95 { 96 if (edge[t].w != prew || t == edgenum + 1) 97 {//处理完长度为prew的所有边后,即一组边加完,对prew的边连接的每个联通块计算生成树个数 98 for (int i = 1, k; i <= n; i++) 99 { 100 if (visit[i]) 101 {//当前长度的边连接了i节点(祖先) 102 k = b.Find(i);//b反映新图的连通关系,a还没更新 103 gra[k].push_back(i);//将i节点压入所属的联通块 104 visit[i] = 0;//清空vis数组 105 } 106 } 107 for (int i = 1; i <= n; i++) 108 {//枚举新图每一个连通分量形成的生成树数目 109 if (gra[i].size())//联通块的点数 110 { 111 memset(C, 0, sizeof(C)); 112 int sz = gra[i].size(); 113 for (int j = 0; j <sz; j++) 114 {//构造该连通块的Kirchhoff矩阵 115 for (int k = j + 1, x, y; k < sz; k++) 116 { 117 x = gra[i][j]; 118 y = gra[i][k]; 119 C[j][k] = C[k][j] = -deg[x][y]; 120 C[j][j] += deg[x][y]; 121 C[k][k] += deg[x][y]; 122 } 123 } 124 ans = ans*DET(C, gra[i].size(), MOD) % MOD; 125 for (int j = 0; j < gra[i].size(); j++) a.pre[gra[i][j]] = i;//缩点,更新并查集a 126 } 127 } 128 memset(deg, 0, sizeof(deg)); 129 for (int i = 1; i <= n; i++) 130 {//连通图缩点,将连通分量并查集的根结点变为一致 131 b.pre[i] = a.Find(i); 132 gra[i].clear(); 133 } 134 if (t == edgenum + 1) break; 135 prew = edge[t].w; 136 } 137 int x = a.Find(edge[t].u); 138 int y = a.Find(edge[t].v); 139 if (x == y) continue; 140 visit[x] = visit[y] = 1;//标记连通块的祖先 141 b.Union(x, y);//使两个分量在一个联通块,更新连通分量用的并查集b,不更新Kruskal的并查集, 在这一阶段结束才更新, 这是为了使得邻接矩阵代表出连通分量之间的关系 142 deg[x][y]++;//邻接矩阵 143 deg[y][x]++; 144 } 145 if (!edgenum) return 0; 146 for (int i = 2; i <= n; i++) 147 if (b.Find(i) != b.Find(1))//图不连通 148 return 0; 149 return ans; 150 } 151 152 int main() 153 { 154 while (~scanf("%d%d%d",&n,&m,&MOD)) 155 { 156 if (n == 0 && m == 0 && MOD == 0)break; 157 edgenum = 0; 158 while (m--) 159 { 160 scanf("%d%d%d", &u, &v, &d); 161 add(u, v, d); 162 } 163 ll ans = cal_MST_count(n, MOD); 164 printf("%d ", ans%MOD); 165 } 166 return 0; 167 }
24、spoj 104 Highways
题意:给出无权无向图,求生成树的个数。
思路:模板题。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int N = 20; 8 typedef long long LL; 9 LL C[N][N]; 10 bool can[N][N]; 11 //对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。 12 //所谓n - 1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。 13 LL det(LL c[][N], int n)//生成树计数:Matrix-Tree定理 14 {//c[N][N]为Kirchhoff矩阵,=它的度数矩阵D减去它的邻接矩阵A(A[i][j]=1表示i和j有边相连) 15 LL ret = 1; 16 for (int i = 1; i<n; i++) 17 { 18 for (int j = i + 1; j<n; j++) 19 while (c[j][i]) 20 { 21 LL t = c[i][i] / c[j][i]; 22 for (int k = i; k<n; k++) 23 c[i][k] = (c[i][k] - c[j][k] * t); 24 for (int k = i; k<n; k++) 25 swap(c[i][k], c[j][k]); 26 ret = -ret; 27 } 28 if (c[i][i] == 0) 29 return 0; 30 ret = ret*c[i][i]; 31 } 32 if (ret<0) 33 ret = -ret; 34 return ret; 35 } 36 37 int main() 38 { 39 int t; 40 scanf("%d", &t); 41 int n, m; 42 while (t--) 43 { 44 scanf("%d%d", &n, &m); 45 memset(C, 0, sizeof(C)); 46 memset(can, false, sizeof(can)); 47 int u, v; 48 while (m--) 49 { 50 scanf("%d%d", &u, &v); 51 u--; 52 v--; 53 can[u][v] = can[v][u] = true; 54 } 55 for (int i = 0; i < n; i++) 56 { 57 for (int j = i + 1; j < n; j++) 58 { 59 if (can[i][j]) 60 { 61 C[i][i]++, C[j][j]++; 62 C[i][j] = -1, C[j][i] = -1; 63 } 64 } 65 } 66 printf("%lld ", det(C, n)); 67 } 68 return 0; 69 }