最小生成树水题大合集
来源:互联网 发布:会声会影x9激活软件 编辑:程序博客网 时间:2024/06/05 15:31
题目一:
hdu - 畅通工程
题意:中文题省略
思路:
裸最小生成树
代码:
#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int G[110][110];int low[110];int vis[110];int N,M;int prim(){ int res=0; vis[1]=1; low[1]=0; for(int i = 2 ; i <= M ; i++) low[i] = G[1][i]; for(int i = 2 ; i <= M ; i++) { int minc = inf; int pos=0; for(int i = 1 ; i <= M ; i++) if(!vis[i] && low[i] < minc ) { minc = low[i]; pos = i; } if(minc == inf) return -1; vis[pos]=1; res += minc; for(int i = 1 ; i <= M ; i++) if(!vis[i] && low[i] > G[pos][i]) low[i] = G[pos][i]; } return res;}int main(){ //freopen("Input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d%d" , &N,&M) && N) { memset(vis,0,sizeof(vis)); memset(G,0x1f,sizeof(G)); memset(low,0x1f,sizeof(low)); for(int i = 0 ; i < N ; i++) { int a,b,w; scanf("%d%d%d",&a,&b,&w); G[a][b] = G[b][a] = w; } int res = prim(); res == -1 ? puts("?") : printf("%d\n" , res); } return 0;}
题目二
poj - Highways
题意:
给你一个图的邻接矩阵,求最小生成树,输出的是树中的最大边
思路:
无脑模板题,矩阵都给了,直接改上题的代码
代码:
#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int G[550][550];int low[550];int vis[550];int m,n;int prim(){ int res=-1; vis[1]=1; low[1]=0; for(int i = 2 ; i <= n ; i++) low[i] = G[1][i]; for(int i = 2 ; i <= n ; i++) { int minc = inf; int pos=0; for(int i = 1 ; i <= n ; i++) if(!vis[i] && low[i] < minc ) { minc = low[i]; pos = i; } vis[pos]=1; res = max(res,minc); for(int i = 1 ; i <= n ; i++) if(!vis[i] && low[i] > G[pos][i]) low[i] = G[pos][i]; } return res;}int main(){ //freopen("Input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); int t; scanf("%d",&t); while(t--) { memset(vis,0,sizeof(vis)); //memset(G,0x1f,sizeof(G)); memset(low,0x1f,sizeof(low)); scanf("%d",&n); for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) scanf("%d" , &G[i][j]); int res = prim(); printf("%d\n" , res); } return 0;}
题目三
poj - Agri-Net
题意:
和上一题几乎完全相同,求的是最小生成树的总权值
思路:
改上一题代码
代码:
#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int G[550][550];int low[550];int vis[550];int m,n;int prim(){ int res=0; vis[1]=1; low[1]=0; for(int i = 2 ; i <= n ; i++) low[i] = G[1][i]; for(int i = 2 ; i <= n ; i++) { int minc = inf; int pos=0; for(int i = 1 ; i <= n ; i++) if(!vis[i] && low[i] < minc ) { minc = low[i]; pos = i; } vis[pos]=1; res += minc; for(int i = 1 ; i <= n ; i++) if(!vis[i] && low[i] > G[pos][i]) low[i] = G[pos][i]; } return res;}int main(){ //freopen("Input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d",&n) && n) { memset(vis,0,sizeof(vis)); //memset(G,0x1f,sizeof(G)); memset(low,0x1f,sizeof(low)); for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) scanf("%d" , &G[i][j]); int res = prim(); printf("%d\n" , res); } return 0;}
题目四:
hdu - 继续畅通工程
#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int n;struct edge{ int s; int e; int w; int state; bool operator < (const edge& b)const { if(state == b.state) return w < b.w; return state > b.state; }}road[10000+100];int set[110];int Find(int x){ return set[x] != x ? (set[x] = Find(set[x])) : x;}int merge(int x , int y){ return set[Find(x)] = Find(y);}int kru(){ int m = (n-1)*n/2; for(int i = 0 ; i <= n ; i++) set[i]=i; sort(road,road+m); int res=0; int cnt=0; for(int i = 0 ; i < m ; i++) { int u = Find(road[i].e); int v = Find(road[i].s); if(u != v) { cnt++; res += road[i].state ? 0 : road[i].w; merge(u,v); } if(cnt == n-1) return res; }}int main(){ //freopen("Input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d" , &n) && n) { int cnt=0; int t = (n-1)*n/2; for(int i = 0 ; i < t ; i++) scanf("%d%d%d%d",&road[i].s , &road[i].e , &road[i].w , &road[i].state); printf("%d\n" , kru()); } return 0;}
题目五
hdu - Connect the Cities
#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int n,m,k;struct edge{ int s; int e; int w; bool operator < (const edge& b)const { return w < b.w; }}road[30000];int f[550];int Find(int x){ return f[x] != x ? (f[x] = Find(f[x])) : x;}int merge(int x,int y){ return ((x=Find(x)) != (y=Find(y))) && (f[x]=y);}int kru(){ sort(road,road+m); int res=0; int cnt=0; for(int i = 1 ; i <= n ; i++) cnt += (f[i]!=i); for(int i = 0 ; i < m ; i++) { int u = Find(road[i].e); int v = Find(road[i].s); if(u != v) { cnt++; res += road[i].w; merge(u,v); } if(cnt == n-1) return res; } return -1;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); int t; scanf("%d" , &t); while(t--) { scanf("%d%d%d" , &n,&m,&k); for(int i = 0 ; i <= n ; i++) f[i]=i; for(int i = 0; i < m ; i++) scanf("%d%d%d",&road[i].s , &road[i].e , &road[i].w); for(int i = 0; i < k ; i++) { int tt; scanf("%d" , &tt); if(tt > 0) { int cur; scanf("%d" , &cur); int ff = Find(cur); for(int j = 1 ; j < tt ; j++) { scanf("%d" , &cur); f[Find(cur)] = ff; } } } printf("%d\n" , kru()); } return 0;}
题目六
poj - Arctic Network
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int cnt;int s,p;struct edge{ int s; int e; double w; bool operator < (const edge& b)const { return w < b.w; }}e[125000+100];struct point{ double x; double y;}pp[550];double dis(point a , point b){ return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));}int f[550];int find( int x ){ return (f[x] != x) ? (f[x] = find(f[x])) : x;}void merger(int a , int b){ int x = find(a); int y = find(b); if(x!=y) f[x] = y;}double kru(){ int cc=0; double res=0; for(int i = 0 ; i < cnt ; i++) { int u = find(e[i].s); int v = find(e[i].e); if( u != v ) { merger(u , v); res = e[i].w; cc++; } if(cc == p-s) return res; } return -1.0;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); int t; scanf("%d" , &t); while(t--) { scanf("%d%d",&s,&p); cnt=0; for(int i = 1 ; i <= p ; i++) { scanf("%lf%lf" , &pp[i].x , &pp[i].y); for(int j = 1 ; j < i ; j++) { e[cnt].s = i; e[cnt].e = j; e[cnt].w = dis(pp[i],pp[j]); cnt++; } } sort(e,e+cnt); for(int i = 0 ; i <= p ; i++) f[i]=i; printf("%.2lf\n" , kru()); } return 0;}
题目七
poj - Truck History
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int G[2010][2010];int vis[2010];int low[2010];int n;char s[2010][10];int calc(char a[] , char b[]){ int cnt=0; for(int i = 0 ; i < 7 ; i++) cnt += (a[i] != b[i]); return cnt;}int prim(){ int res=0; vis[1]=1; low[1]=0; for(int i = 2 ; i <= n ; i++) low[i] = G[1][i]; for(int i = 2 ; i <= n ; i++) { int minc = inf; int pos = 0; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && low[j] < minc) { pos = j; minc = low[j]; } } vis[pos]=1; res+=minc; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && low[j] > G[pos][j]) low[j] = G[pos][j]; } } return res;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d",&n) && n) { memset(low,0x1f,sizeof(low)); memset(vis,0,sizeof(vis)); memset(G , 0x1f , sizeof(G)); for(int i = 1 ; i <= n ; i++) { scanf("%s",s[i]); for(int j = 1 ; j < i ; j++) { G[i][j] = G[j][i] = calc(s[i],s[j]); } } printf("The highest possible quality is 1/%d.\n" ,prim()); } return 0;}
题目八
hdu - Tree
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int n;int p[650];int G[650][650];int low[650];int vis[650];int prime[2000100];void init(){ prime[0]=prime[1]=1; prime[2]=0; for(int i = 2 ; i <= 2000000 ; i++) if(!prime[2]) { for(int j = i*2 ; j <= 2000000; j+=i) prime[j]=1; }}int w(int a , int b){ if(!prime[a] || !prime[b] || !prime[a+b]) return min(min(a,b),abs(a-b)); return inf;}LL prim(){ vis[1]=1; low[1]=0; LL res=0; for(int i = 2 ; i<= n ; i++) low[i] = G[1][i]; for(int i = 2 ; i <= n ; i++) { int minc = inf; int pos=0; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && low[j] < minc) { pos=j; minc = low[j]; } } vis[pos] = 1; res+=minc; if(minc == inf) return -1; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && low[j] > G[pos][j]) { low[j] = G[pos][j]; } } } return res;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); int t; scanf("%d" , &t); init(); while(t--) { memset(G,0x1f,sizeof(G)); memset(vis,0,sizeof(vis)); memset(low,0x1f,sizeof(low)); scanf("%d" , &n); for(int i = 1 ; i <= n ; i++) { scanf("%d" , p+i); for(int j = 1 ; j < i ; j++) G[i][j] = G[j][i] = w(p[i],p[j]); } cout << prim() << endl; } return 0;}
题目九
hdu - Fibonacci Tree
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int fi[1000000];int n,m;struct edge{ int s; int e; int w; bool operator < (const edge& b)const { return w > b.w; }}e[100000+100];void init(){ int a=1,b=1,c=2; fi[1]=1; do { fi[c]=1; a=b; b=c; }while((c = a+b) < 1000000);}int pos;int f[100000+100];int find(int x){ return (x == f[x]) ? x : f[x] = find(f[x]);}void meger(int x , int y){ x = find(x); y = find(y); if(x!=y) f[x]=y;}int kru(int st , int dir){ for(int i = 0 ; i <= n ; i++) f[i]=i; int cnt=0; int res=0; for(int i = st ; i>0&&i<=m ; i+=dir) { int u = find(e[i].s); int v = find(e[i].e); if(u!=v) { cnt++; res+=e[i].w; meger(u,v); } if(cnt == n-1) break; } if(cnt < n-1) return -1; return res;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); int t; scanf("%d" , &t); init(); for(int ka = 1 ; ka <= t ; ka++) { scanf("%d%d",&n,&m); for(int i = 1 ; i <= m ; i++) scanf("%d%d%d",&e[i].s,&e[i].e,&e[i].w); sort(e+1,e+m+1); int r = kru(1,1); printf("Case #%d: ",ka); if(r == -1) { puts("No"); continue; } int l = kru(m,-1); int ok=0; for(int i = l; i <= r; i++) if(fi[i]) { ok=1; break; } ok ? puts("Yes") : puts("No"); } return 0;}
题目十:
hdu - Minimal Ratio Tree
思路:
很有意思的一道题,刚开始我是直接遍历每一个顶点,以每一个顶点为起点prim,当记录的顶点数为m时跳出,更新最小值及相关项,结果wa了,后来想想有可能边权之和最小了但是点权之和不是最大的,而且这样会遗漏情况,直接以除式形式为权值又不好建树,所以改了方法:
由于数据量很小,所以我先dfs获取所有顶点数为m的集合,然后在这个集合中选点建树,由于一个集合中点权之和一定是个定值,所以只要考虑边权之和最小就可以了,而这就是标准的最小生成树。这样枚举完所有情况,也不会出现遗漏了。
代码如下:
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "map"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;int m,n;int node[20];int G[20][20];int vis[20];double low[20];vector<int> out;double ans;void prim(vector<int> cur){ memset(vis,0,sizeof(vis)); memset(low,0x1f,sizeof(low)); vis[cur[0]]=1; low[cur[0]]=0; double fenzi=0 , fenmu=0; for(int i = 0 ; i < cur.size() ; i++) fenmu += node[cur[i]]; for(int i = 0 ; i < m ; i++) low[cur[i]] = G[cur[0]][cur[i]]; for(int i = 1 ; i < m ; i++) { int minc = inf; int pos = 0; for(int j = 0 ; j < m ; j++) { if(!vis[cur[j]] && low[cur[j]] < minc) { minc = low[cur[j]]; pos = cur[j]; } } vis[pos] = 1; fenzi += minc; for(int j = 0 ; j < m ; j++) if(!vis[cur[j]] && low[cur[j]] > G[pos][cur[j]]) low[cur[j]] = G[pos][cur[j]]; } if(ans > fenzi/fenmu + 1e-8) { out = cur; ans = fenzi/fenmu; } return ;}void dfs(vector<int> cur , int st){ if(cur.size() == m) { prim(cur); return; } for(int i = st+1 ; i <= n ; i++) { cur.push_back(i); dfs(cur,i); cur.pop_back(); }}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d%d" , &n,&m) && (m||n)) { ans = inf; for(int i = 1 ; i <= n ; i++) scanf("%d" , node+i); for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) scanf("%d" , &G[i][j]); vector<int> zouqi; dfs(zouqi,0); sort(out.begin() , out.end()); for(int i = 0 ; i < out.size() ; i++) { if(i==0) printf("%d" , out[i]); else printf(" %d" , out[i]); } puts(""); } return 0;}
题目十一:
poj - Slim Span
题意:
给一个无向图,求一颗生成树,它的最大边减去最小边的值最小
思路:
暴力kruscal,具体看代码一目了然
代码:
#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "algorithm"#include "cmath"#include "cstdio"#include "sstream"#include "queue"#include "vector"#include "string"#include "stack"#include "cstdlib"#include "deque"#include "fstream"#include "map"#define eps 1e-14using namespace std;typedef long long LL;const int MAXN = 1000000+100;const int INF = 0x6fffffff;const int inf = 522133279;const long long LLinf = 1e30;struct edge{ int s; int e; int w; bool operator < (const edge& b)const { return w < b.w; }}e[10010];int f[110];int n,m;int ans;int find(int x){ return f[x] == x ? x : f[x] = find(f[x]);}void merger(int x , int y){ x = find(x); y = find(y); if(x != y) f[x] = y;}void kru(int st){ for(int i = 0 ; i <= n ; i++) f[i] = i; int cnt=0; int minc=inf; int maxc = -1; for(int i = st ; i < m ; i++) { int x = find(e[i].s); int y = find(e[i].e); if(x != y) { cnt++; merger(x,y); minc = min(minc , e[i].w); maxc = max(maxc , e[i].w); } if(cnt == n-1) { ans = min(ans,maxc - minc); return ; } }}int main(){ //freopen("input.txt" , "r" , stdin); while(~scanf("%d%d" , &n,&m) && (n||m)) { ans = inf; for(int i = 0 ; i < m ; i++) scanf("%d%d%d" , &e[i].s,&e[i].e,&e[i].w); sort(e,e+m); for(int i = 0 ; i <= m-n+1 ; i++) kru(i); if(ans == inf) ans = -1; printf("%d\n" , ans); } return 0;}
题目十二:
poj 2031 - Building a Space Station
题意:
输入一行四个数的是一个球的三维坐标加半径,两个球能相互触碰到的话权值为0,否则就要用边去连接这两个球,边的长度是球面之间最小距离
思路:
无脑模板题不解释
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "vector"#include "map"#include "stack"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;const double eps = 1e-10;int n;struct ball{ double x,y,z; double r;}Ball[110];double dis(ball a , ball b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}double g[110][110];int vis[110];double low[110];double prim(){ memset(vis,0,sizeof(vis)); memset(low,0x1f,sizeof(low)); vis[1]=1; low[1]=0; double res=0; for(int i = 1 ; i <= n ; i++) low[i] = g[1][i]; for(int i = 2 ; i <= n ; i++) { double minc = inf; int pos = 0; for(int j = 1 ; j <= n ; j++) if(!vis[j] && low[j] < minc) { minc = low[j]; pos = j; } vis[pos]=1; res += minc; for(int j = 1 ; j <= n ; j++) if(!vis[j] && low[j] > g[pos][j]) low[j] = g[pos][j]; } return res;}int main(){ //freopen("input.txt" , "r" , stdin); //freopen("out.txt","w",stdout); while(~scanf("%d" , &n) && n) { memset(g,0x1f,sizeof(g)); for(int i = 1 ; i <= n ; i++) { scanf("%lf%lf%lf%lf" , &Ball[i].x , &Ball[i].y , &Ball[i].z , &Ball[i].r); for(int j = 1; j < i ; j++) { double tmpd = dis(Ball[i] , Ball[j]); g[i][j] = g[j][i] = tmpd > Ball[i].r + Ball[j].r ? tmpd - (Ball[i].r + Ball[j].r) : 0; } } printf("%.3lf\n" , prim()); } return 0;}
题目十三:
hdu 4081 - Qin Shi Huang's National Road System(次小生成树,重要)
徐福能够让一条路的消耗为0,称之为魔法路
秦始皇想让总权值最小,徐福想让魔法路连接的两个城市的总人口最大
于是为了折中,令A = 魔法路连接的两个城市的总人口,B = 总权值-魔法路的原权值(即这条路权值为0之后的总权值)
要求A/B最大
思路:
构造生成树无疑,为了使A/B尽可能大,在A能够达到所有组合的情况下,B要尽量小,所以构造的是最小生成树,总权值为quanzhi
然后枚举所有边,分两种情况:
1,这条边在树上,那么分母就是quanzhi - w;
2,这条边不在树上,那么加上这条边之后一定会出现一个环,这条加上的路反正会变成0我们不管,现在要关注的是这棵树已经不是树了,也就是说
多造了无用边,秦始皇哪有这么笨……所以我们要去掉原树上的一条边。假设加入边连通城市a和b,为了保证连通性,肯定是去掉a-b中的一条边,
仍旧是B最小原则,去掉的就是连接a,b的唯一通路的一条权值最大的边。综上,此情况下B = quanzhi - g[a][b]
为什么要进行步骤2?因为我们需要让A尽可能大,选择的城市可能不是在树上直接连通的
于是发现就是求最小生成树的步骤
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "string"#include "cmath"#include "queue"#include "cstdio"#include "algorithm"#include "cctype"#include "vector"#include "map"#include "stack"using namespace std;typedef long long LL;const int INF = 0x6fffffff;const int inf = 522133279;const LL llinf = 1e30;const double eps = 1e-10;int n;struct City{ int x; int y; int people;}city[1010];double g[1010][1010];int vis[1010];double low[1010];int tree[1010][1010]; //tree[i][j] 表示i - j这条边是不是在树上,是为1double maxedge[1010][1010]; //maxedge[i][j] 生成树上连接i-j的唯一路径中的最大边int pre[1010]; //pre[i]表示i点之前的那一点,由构造顺序决定double dis(City a , City b){ return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));}double prim(){ memset(maxedge, 0 , sizeof(maxedge)); memset(vis,0,sizeof(vis)); memset(low,0x1f,sizeof(low)); memset(tree,0,sizeof(tree)); double res=0; vis[1]=1; low[1]=0; pre[1]=1; for(int i = 2 ; i <= n ; i++) low[i] = g[1][i],pre[i]=1; for(int i = 2 ; i <= n ; i++) { double minc = inf; int pos =0; for(int j = 1 ; j <= n ; j++) if(!vis[j] && low[j] < minc) { pos = j; minc = low[j]; } vis[pos]=1; res += minc; maxedge[pre[pos]][pos] = maxedge[pos][pre[pos]] = g[pos][pre[pos]]; //相邻两点只有这一种权值了 tree[pre[pos]][pos] = tree[pos][pre[pos]] = 1; //边被加入树中 for(int j = 1 ; j <= n ; j++) if(!vis[j] && low[j] > g[pos][j]) { low[j] = g[pos][j]; pre[j] = pos; //在刷新j到生成树的最小距离的同时,也要刷新j的前驱结点 } for(int j = 1 ; j <= n ; j++) if(vis[j] && j != pos) //如果j在树中且不是新加入的点,刷新 //最大边权就是j到pos前驱这个区间中最大的权值和新加的边的权值的较大一个,这里有点dp的意思 //注意不要被最小生成树构造法则误导:low[i]是i点到当前生成树的最小距离,同样maxedge也是这么构造起来的,有可能最小的边不能加入生成树,因为没有关联的顶点 //所以不要简单地认为low[pos]一定是最大的边 //当然kruskal是严格按照边的大小来的 maxedge[j][pos] = maxedge[pos][j] = max(maxedge[j][pre[pos]] , low[pos]); //需要说明的是low[i] == maxedge[i][pre[i]],因为只有这条边,没得选择 } return res;}int main(){ int t; scanf("%d" , &t); while(t--) { memset(g,0,sizeof(g)); scanf("%d" , &n); for(int i = 1 ; i <= n ; i++) { scanf("%d%d%d" , &city[i].x,&city[i].y,&city[i].people); for(int j = 1 ; j < i ; j++) g[i][j] = g[j][i]= dis(city[i],city[j]); } double quanzhi = prim(); double maxc = 0; for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) { if(i==j) continue; if(!tree[i][j]) maxc = max(maxc , 1.0*(city[i].people+city[j].people) / (quanzhi - maxedge[i][j])); else maxc = max(maxc , 1.0*(city[i].people+city[j].people) / (quanzhi - g[i][j])); } printf("%.2lf\n" , maxc); } return 0;}
题目十四:
poj 1679 - The Unique MST
题意:
给一个图让你判断这个图的最小生成树是否唯一
思路:
做过上一题之后这题就显得很水了,就是一个很裸的次小:
先构造一颗最小生成树,然后用不在树中的边替换某位一路径中的最大段,假设这两短长度相同,那么就找到了另一颗最小生成树
代码:
#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "algorithm"#include "cmath"#include "cstdio"#include "sstream"#include "queue"#include "vector"#include "string"#include "stack"#include "cstdlib"#include "deque"#include "fstream"#include "map"#define eps 1e-14using namespace std;typedef long long LL;const int MAXN = 1000000+100;const int INF = 0x6fffffff;const int inf = 522133279;const long long LLinf = 1e30;int n,m;int g[110][110];int low[110];int vis[110];int maxedge[110][110];int tree[110][110];int pre[110];int prim(){ memset(low,0x1f,sizeof(low)); memset(vis,0,sizeof(vis)); memset(maxedge,0,sizeof(maxedge)); memset(tree,0,sizeof(tree)); low[1]=0; vis[1]=1; pre[1]=1; int res=0; for(int i = 2 ; i <= n ; i++) low[i] = g[1][i] , pre[i]=1; for(int i = 2 ; i <= n ; i++) { int minc = inf; int pos = 0; for(int j = 1 ; j <= n ; j++) if(!vis[j] && low[j] < minc) { minc = low[j]; pos = j; } vis[pos]=1; res+=minc; maxedge[pos][pre[pos]] = maxedge[pre[pos]][pos] = g[pos][pre[pos]]; tree[pos][pre[pos]] = tree[pre[pos]][pos] = 1; for(int j = 1 ; j <= n ; j++) { if(!vis[j] && low[j]>g[pos][j]) low[j] = g[pos][j] , pre[j] = pos; } for(int j = 1 ; j <= n ; j++) { if(vis[j] && j!=pos) maxedge[pos][j] = maxedge[j][pos] = max(maxedge[pre[pos]][j],low[pos]); } } return res;}int main(){ int t; scanf("%d" , &t); while(t--) { memset(g,0x1f,sizeof(g)); scanf("%d%d" , &n,&m); for(int i = 0 ; i < m ; i++) { int a,b,c; scanf("%d%d%d" , &a,&b,&c); g[a][b] = g[b][a] = c; } int ok=1; int quanzhi = prim(); for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j < i ; j++) //这里用更简单的判断边是否在树上的方法,即通过pre判断i,j是否直接相连,这样就不用设tree数组了 if(/*!tree[i][j]*/i != pre[j] && j != pre[i] && g[i][j] == maxedge[i][j]) { ok = 0; break; } if(!ok) break; } if(ok) printf("%d\n" , quanzhi); else puts("Not Unique!"); } return 0;}
题目十五:
poj 3026 - Borg Maze(有趣的建图,poj神坑题)
题意:
有一群人要消灭藏在迷宫里的外星人,这群人可以在开始或者找到外星人之后分成数波朝不同方向走,走的路径总和是各个队伍的路径和(当然中途分叉的队伍在分叉前算一次路径),求最小路径总和
思路:
这题要是不能分叉,那就是dfs的问题,但是能分叉的话,显然就是一个最小生成树的问题,难就难在建图,建图我用bfs
用一个数组aline记录字母的标号(id),即顶点编号,为避免重复,每一个顶点(x,y)的索引是(x * 行数 + y)
由于是最小生成树,那就不用管什么起点终点了。哪出发都一样
注意:
为什么说这题是神坑题......因为x y之后还可能出现若干空格!!!
wa了2次,然后看了poj的discuss把getchar()改成while(getchar()!='\n');就过了...................擦
代码:
#pragma comment(linker, "/STACK:102400000,102400000")#include "iostream"#include "cstring"#include "algorithm"#include "cmath"#include "cstdio"#include "sstream"#include "queue"#include "vector"#include "string"#include "stack"#include "cstdlib"#include "deque"#include "fstream"#include "map"#define eps 1e-14using namespace std;typedef long long LL;const int MAXN = 1000000+100;const int INF = 0x6fffffff;const int inf = 522133279;const long long LLinf = 1e30;char maze[110][110];int g[110][110];int low[110];int vis[110];int dir[4][2] = {0,1,1,0,-1,0,0,-1};int aline[3000];struct node{ int x; int y; int path; node(){x=0,y=0,path=0;} node(int _x , int _y , int _path) { x = _x; y = _y; path = _path; }};int hang,lie;int id;int border(int x , int y){ return (x >= 0 && x < hang) && (y >= 0 && y < lie);}void bfs(node s){ int use[51][51]; memset(use,0,sizeof(use)); use[s.x][s.y]=1; queue<node> que; while(!que.empty()) que.pop(); que.push(s); node tmp,pre; int sid = s.x*hang+s.y; while(!que.empty()) { pre = que.front(); que.pop(); for(int i = 0 ; i < 4 ; i++) { int x = pre.x + dir[i][0]; int y = pre.y + dir[i][1]; if(border(x,y) && !use[x][y] && maze[x][y] != '#') { use[x][y]=1; tmp.x = x; tmp.y = y; tmp.path = pre.path+1; que.push(tmp); if(isalpha(maze[x][y])) g[aline[sid]][aline[x*hang+y]] = tmp.path; } } }}int prim(){ memset(low,0x1f,sizeof(low)); memset(vis,0,sizeof(vis)); vis[0]=1; low[0]=0; int res=0; for(int i = 1 ; i < id ; i++) low[i] = g[0][i]; for(int i = 1 ; i < id ; i++) { int minc = inf; int pos=0; for(int j = 0 ; j < id ; j++) { if(!vis[j] && low[j] < minc) { minc = low[j]; pos = j; } } vis[pos] = 1; res += minc; for(int j = 0 ; j < id ; j++) { if(!vis[j] && low[j] >g[pos][j]) low[j] = g[pos][j]; } } return res;}int main(){ int t; scanf("%d",&t); getchar(); while(t--) { memset(g,0x1f,sizeof(g)); memset(aline,0,sizeof(aline)); scanf("%d%d",&lie,&hang); while(getchar()!='\n') ; id=0; for(int i = 0 ; i < hang ; i++) { gets(maze[i]); for(int j = 0 ; maze[i][j] ; j++) if(isalpha(maze[i][j])) aline[i*hang+j] = id++; } for(int i = 0 ; i < hang ; i++) for(int j = 0 ; j < lie ; j++) if(isalpha(maze[i][j])) bfs(node(i,j,0)); printf("%d\n" , prim()); } return 0;}
- 最小生成树水题大合集
- 最小比例 最小生成树
- 最小生成树&&次最小生成树
- 最小生成生成树计数
- 最小生成树
- 最小生成树 MST
- 最小生成树Kruskal
- kruskal 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树 MST
- 最小生成树问题
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 三、创建 event_base
- IOS UILabel 文字描边详解
- 如何绘制precision——recall曲线和confusion矩阵
- 最小树形图
- (*)mongodb 安装及参数说明
- 最小生成树水题大合集
- Json字符串转List集合
- 用VC++实现应用程序窗口的任意分割(1)
- vc6.0中的dsp,dsw,ncb,opt,clw,plg,aps等文件的简单说明
- 窗体传值
- java多线程之Condition的使用
- c++11 eclipse解析stl需要引入的头文件
- 写给那些看不懂委托的同学
- 证件照缩小为20k大小