最小生成树
最小生成树,基于并查集,把边权排序,看父节点不相同就连,最大生成树一样的。
https://vjudge.net/contest/280903#problem/A习题集HDu1102
下面是最简单的一种。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxm = 105; int pa[maxm], rak[maxm]; int dis[maxm][maxm]; struct node { int x, y, dis; } p[maxm * maxm]; int comp(node n1, node n2) { return n1.dis < n2.dis; } void make_set(int x) { pa[x] = x; rak[x] = 0; } int findx(int x) { int r = x, temp; while(pa[r] != r) r = pa[r]; while(x != r) { temp = pa[x]; pa[x] = r; x = temp; } return x; } void unio(int x, int y) { x = findx(x); y = findx(y); if(x == y)return ; if(rak[x] > rak[y]) { pa[y] = x; } else { pa[x] = y; if(rak[x] == rak[y]) rak[y]++; } } int n, m, x, y, nn; int main() { while(~scanf("%d", &n)){ int ant = 0; for(int i = 1; i <= n; i++) { make_set(i); } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { p[ant].x = i; p[ant].y = j; scanf("%d", &nn); p[ant].dis = nn; ant++; } } scanf("%d", &m); while(m--) { scanf("%d%d", &x, &y); unio(x, y); } sort(p, p + n * n, comp); int res = 0; for(int i = 0; i < n * n; i++) { if(findx(p[i].x) != findx(p[i].y)) { res += p[i].dis; unio(p[i].x, p[i].y); } } printf("%d\n" ,res); } return 0; }
Slim Span
find the most comfortable road
这两个题目都是求一条最小生成树,然后对权值差有所要求。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxm = 205; const int maxm1 = 1005; typedef pair<double, double> pii; typedef long long ll; int pa[maxm], rak[maxm]; int num[maxm]; struct node { int x, y; int dis; } p[maxm1]; pii point[maxm]; int comp(node n1, node n2) { return n1.dis < n2.dis; } void make_set(int x) { pa[x] = x; rak[x] = 0; } int findx(int x) { int r = x, temp; while(pa[r] != r) r = pa[r]; while(x != r) { temp = pa[x]; pa[x] = r; x = temp; } return x; } void unio(int x, int y) { x = findx(x); y = findx(y); if(x == y)return ; if(rak[x] > rak[y]) { pa[y] = x; } else { pa[x] = y; if(rak[x] == rak[y]) rak[y]++; } } int n, m, k; double d; char ch[3]; int main() { while(~scanf("%d%d", &n, &m)) { memset(num, 0, sizeof(num)); for(int i = 0; i < m; i++) { scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].dis); // num[ p[i].x ]++; // num[ p[i].y ]++; } sort(p, p + m, comp); scanf("%d", &k); int l, r; while(k--) { scanf("%d%d", &l, &r); int minn = 1e9 + 7; int flag = 0; for(int i = 0; i < m; i++) { for(int i = 1; i <= n; i++) { make_set(i); } for(int j = i; j < m; j++) { unio(p[j].x, p[j].y); if(findx(l) == findx(r)) { minn = min(minn, p[j].dis - p[i].dis); flag = 1; break; } } }
//这题是看L到R是否联通,所以先排序,然后枚举最小距离,在循环找到最大距离,在相减。 if(flag) printf("%d\n", minn); else printf("-1\n"); } } return 0; }
https://vjudge.net/contest/280903#problem/EHDu3367
Pseudoforest
伪森林的定义
1,一个无向图;
2,它的所有连通分量最多只有一个环;
3. 伪森林的大小取决于图中所有边的边权之和。
题意:给你一个无向图,让你求出图中最大的伪森林。
这道题用到最大生成树的实现方法(kruskal)。关键在于并查集部分的处理
一:两点不在一颗树上
1,两点都不在环里,直接增边;
2,两点只有一个在环里,增边后并标记两点都在环里面;
3,两点都在环里面,增边后生成树一定会有至少两个环,不符;
二:两点在一棵树上
1,两点都不在环里,直接增边,并标记;
2,两点只有一个在环里,增边后生成树会有至少两个环,不符;
3,两点都在环里面,增边后生成树一定会有至少两个环,不符;
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxm = 1e4 + 5; const int maxm1 = 1e5 + 6; typedef pair<double, double> pii; typedef long long ll; int pa[maxm], rak[maxm]; int vis[maxm]; struct node { int x, y; int dis; } p[maxm1]; pii point[maxm]; int comp(node n1, node n2) { return n1.dis > n2.dis; } void make_set(int x) { pa[x] = x; rak[x] = 0; } int findx(int x) { int r = x, temp; while(pa[r] != r) r = pa[r]; while(x != r) { temp = pa[x]; pa[x] = r; x = temp; } return x; } void unio(int x, int y) { x = findx(x); y = findx(y); if(x == y)return ; if(rak[x] > rak[y]) { pa[y] = x; } else { pa[x] = y; if(rak[x] == rak[y]) rak[y]++; } } int n, m; double d; char ch[3]; int main() { while(~scanf("%d%d", &n, &m)) { if(n == 0 && m == 0) break; memset(vis, 0, sizeof(vis)); for(int i = 0; i < n; i++) { make_set(i); } for(int i = 0; i < m; i++) { scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].dis); } sort(p, p + m, comp); ll res = 0; for(int i = 0; i < m; i++) { if(findx(p[i].x) != findx(p[i].y)) { if(vis[ findx(p[i].x) ] && vis[ findx(p[i].y) ]) continue; else if((vis[ findx(p[i].x) ] && !vis[ findx(p[i].y)] ) || (!vis[ findx(p[i].x) ] && vis[ findx(p[i].y)] )) { vis[ findx(p[i].x) ] = 1; vis[ findx(p[i].y) ] = 1; res += p[i].dis; unio(p[i].x, p[i].y); } else { res += p[i].dis; unio(p[i].x, p[i].y); } } else { if(!vis[ findx(p[i].x) ]) { res += p[i].dis; unio(p[i].x, p[i].y); vis[ findx(p[i].x) ] = 1; } } } printf("%lld\n", res); } return 0; }
Buy or Build
难题,有现成的已经连在一起的,有固定的价格,其他两个连在一起的价格是他们距离的平方,然后求最小生成树的消费。
其中有个二进制枚举#include<cstdio>
#include<algorithm> #include<cstring> using namespace std; using namespace std; const int maxm = 1005; int t, n , m, rec, q, cnt, pa[maxm], x[maxm],y[maxm]; struct node { int n , c, a[maxm];//结构体里面存数组 }p[9]; struct edge{ int a, b, v; }ed[maxm * maxm], e[maxm]; bool comp(edge a, edge b) { return a.v < b.v; } int findx(int x) { return pa[x] == x ? x : pa[x] = findx(pa[x]); } int solve() { sort(ed, ed + cnt, comp); int ans = 0; rec = 0;
//先拿到初始的最小生成树的大小。 for(int i = 0; i < cnt; i++) { int x = findx(ed[i].a) , y = findx(ed[i].b); if(x != y) { ans += ed[i].v; e[rec].a = ed[i].a; e[rec].b = ed[i].b; e[rec].v = ed[i].v; rec++; pa[x] = y; } }
//二进制枚举。
//第一个for循环是为了得到所有的情况,用二进制表示,第二个是为了把1提取出来。 for(int s = 0; s < (1 << q); s++) { for(int j = 1; j <= n; j++) pa[j] = j; int cur = 0; for(int j = 0; j < q; j++) {
if(s & (1 << j)) { cur += p[j].c; for(int i = 1; i <= p[j].n; i++) { int x = findx(p[j].a[i]) ,y = findx(p[j].a[1]); if(x != y) pa[x] = y; } } }
//补边 for(int i = 0; i < rec; i++) { int x = findx(e[i].a) , y = findx(e[i].b); if(x != y) { cur += e[i].v; pa[x] = y; } } ans = min(ans,cur); } return ans; } int main() { scanf("%d", &t); while(t--) { scanf("%d%d", &n, &q); for(int i = 0; i < q; i++) { scanf("%d%d", &p[i].n, &p[i].c); for(int j = 1; j <= p[i].n; j++) scanf("%d", &p[i].a[j]); } for(int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]); cnt = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { int v = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]); ed[cnt].a = i; ed[cnt].b = j; ed[cnt].v = v; cnt++; } } for(int i = 1; i <= n; i++) pa[i] = i; printf("%d\n",solve()); if(t) printf("\n"); } return 0; }
https://blog.csdn.net/yuanhanchun/article/details/76735225 prime算法,更克鲁斯卡尔差不多