第二周---最小生成树(Kruskal,Prim)、拓扑排序
来源:互联网 发布:jquery weui.min.js 编辑:程序博客网 时间:2024/05/21 02:49
训练链接:
https://vjudge.net/contest/159297#rank
poj 2403
题意:
给出职位名称,该职位的工资,然后给出一个工作的描述,要求出能给出多少工资。
分析:
字符串模拟,判断一段话中有没有该职位名称,有的话就把工资加上。
AC代码:
#include <stdio.h>#include <map>#include <iostream>using namespace std;map <string,int> mm;char ch[100005];int main(){ int n,m; int i,j; char str[20]; int money; while(scanf("%d %d",&n,&m)!=EOF) { while(!mm.empty()) mm.clear(); for(i=0;i<n;i++) { scanf("%s %d",str,&money); mm[str] = money; } for(i=0;i<m;i++) { long long sum=0; while(scanf("%s",ch),ch[0]!='.') { if(mm[ch]!=NULL) sum+=mm[ch]; } printf("%I64d\n",sum); } } return 0;}
poj 2419
题意:
给出p个人,t棵树,每个人都会听到树倒下的声音,当且仅当两个人听到倒下的树全都相同,则说明两个人在一起(或者说在一个集合里)
分析:
该题目就是判断两个集合是否相同,数据量比较小,直接循环判断是否每个标志的值都相同。
AC代码:
#include <stdio.h>#include <string.h>int main(){ int n,m; int a,b,c; int i,j,k; int dp[105][105]; int vis[105]; memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); scanf("%d %d",&n,&m); while(scanf("%d %d",&a,&b)!=EOF) dp[a][b]=1; int cnt=0; for(i=1;i<=n;i++) { if(vis[i]==0) { for(j=i+1;j<=n;j++) { if(vis[j]==0) { int peace=0; for(k=1;k<=m;k++) { if(dp[i][k]!=dp[j][k]) { peace=1; break; } } if(!peace) { vis[j]=1; } } } vis[i]=0; cnt++; } } printf("%d\n",cnt); return 0;}
训练链接:
https://vjudge.net/contest/25317#overview
poj 1251
题意:
给出n个点,任意条路,求出连接这n个点最少需要的路径长度。
分析:
最小生成树的模板题,用的Kruskal写的,只是在Poj上一直TLE,原因在于输入和输出的问题。这里强哥说,对于有空格的这样的字符,可以用%s来读入处理。之前我用的%c 和 %*c来处理的字符的输入,可能会出很多问题。或者可以用cin来读入数据,也会省略掉读入空格和回车的问题。
AC代码:
#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>#define N 105using namespace std;struct node{ int u,v,w;};node edge[N];int cnt=0;int pre[28];int cmp(node a,node b){ return a.w<b.w;}int find(int x){ int r = x; while(r!=pre[r]) r = pre[r]; int i=x,j; while(i!=r) { j = pre[i]; pre[i] = r; i = j; } return r;}void Kruskal(){ int i,j; int sum=0; sort(edge,edge+cnt,cmp); for(i=0;i<cnt;i++) { int px = find(edge[i].u); int py = find(edge[i].v); if(px!=py) { pre[py] = px; sum+=edge[i].w; } } printf("%d\n",sum);}int main(){ int i,j; int m,n; char ch1,ch2; int w; //scanf("%d%*c",&n),n while(cin>>n,n) { for(i=0;i<=27;i++) pre[i]=i; cnt=0; for(j=0;j<n-1;j++) { //scanf("%c %d%*c",&ch1,&m); cin>>ch1>>m; for(i=0;i<m;i++,cnt++) { //scanf("%c %d%*c",&ch2,&w); cin>>ch2>>w; edge[cnt].u = ch1-'A'; edge[cnt].v = ch2-'A'; edge[cnt].w = w; } } Kruskal(); } return 0;}
poj 1287
题意:
给出n个点,m条边,求出连接这n个点所需要的最短路径。
分析:
这个题目要求输入的边的个数没有限制,n最大为50,所以,更合适的应该用邻接矩阵来存储,重复输入的边,需要判断是否最短,再决定是否更新,然后转换成边的结构体,再进行Kruskal就可以了。因为数据水,我就开了10^8的数组,也过了。
AC代码:
#include <stdio.h>#include <algorithm>#include <string.h>using namespace std;struct node{ int u,v,w;};int n,m;int pre[55];node edge[10000005];int cmp(node a,node b){ return a.w<b.w;}int find(int x){ int r = x; while(r!=pre[r]) r = pre[r]; int i=x,j; while(i!=r) { j = pre[i]; pre[i] = r; i = j; } return r;}void Kruskal(){ int i,j; int sum=0; sort(edge,edge+m,cmp); for(i=0;i<m;i++) { int px = find(edge[i].u); int py = find(edge[i].v); if(px!=py) { sum+=edge[i].w; pre[py] = px; } } printf("%d\n",sum);}int main(){ int i,j; while(scanf("%d",&n),n) { for(i=0;i<=n;i++) pre[i]=i; scanf("%d",&m); for(i=0;i<m;i++) scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w); Kruskal(); } return 0;}
poj 2031
题意:
给出n个空间站,要求出把所有空间站连起来的最短路径。给出空间站的x、y、z坐标和r半径。
分析:
这个题目也是最小生成树,只是把路径转换成了球体和球体之间的距离,把输入的数据处理一下就可以。
AC代码:
#include <stdio.h>#include <string.h>#include <math.h>#include <algorithm>using namespace std;struct node{ double x,y,z; double r;};struct Edge{ int u,v; double w;};Edge edge[10005];int pre[105];node circle[105];int cnt = 0;int cmp(Edge a,Edge b){ return a.w<b.w;}double dis(node a,node 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));}int find(int x){ int r = x; while(r!=pre[r]) r = pre[r]; int i =x,j; while(i!=r) { j = pre[i]; pre[i] = r; i = j; } return r;}void Kruskal(){ int i,j; double sum=0.0; sort(edge,edge+cnt,cmp); for(i=0;i<cnt;i++) { int px = find(edge[i].u); int py = find(edge[i].v); if(px!=py) { pre[py] = px; sum+=edge[i].w; } } printf("%.3f\n",sum);}int main(){ int n; int i,j; while(scanf("%d",&n),n) { cnt=0; for(i=0;i<n;i++) pre[i]=i; for(i=0;i<n;i++) scanf("%lf %lf %lf %lf",&circle[i].x,&circle[i].y,&circle[i].z,&circle[i].r); for(i=0;i<n;i++) { for(j=i+1;j<n;j++) { double temp = dis(circle[i],circle[j]); edge[cnt].u = i; edge[cnt].v = j; if(temp > circle[i].r + circle[j].r) edge[cnt].w = temp - (circle[i].r + circle[j].r); else edge[cnt].w = 0.0; cnt++; } } Kruskal(); } return 0;}
训练链接:
https://vjudge.net/contest/25317
poj 2421
题意:
给出n个村庄,给出一个邻接矩阵,然后给出q个已经相邻的村庄,求出把所有村庄相连还需要最少修多长的路。
分析:
最小生成树问题,这次用的Prim算法。
算法思想:
Prim算法是以结点来加入的,每次找一个与该生成树最近的结点,加入到集合中,再松弛,加入该点后所有未加入的结点的最短路是否发生变化,更新。
该算法类似于Dijkstra算法,只是松弛部分有些区别而已。
AC代码:
#include <stdio.h>#include <string.h>#define INF 1<<30int map[105][105];int n;int dis[105];int vis[105];void prim(){ int i,j; memset(vis,0,sizeof(vis)); for(i=0;i<=n;i++) dis[i] = map[1][i]; dis[1]=0; vis[1]=1; int sum=0; int mark; for(i=0;i<n-1;i++) { int min=INF; for(j=1;j<=n;j++) { if(!vis[j] && dis[j] < min) { min = dis[j]; mark = j; } } vis[mark]=1; sum += dis[mark]; if(min==INF) break; for(j=1;j<=n;j++) { if(!vis[j] && dis[j] > map[mark][j] ) dis[j] = map[mark][j]; } } printf("%d\n",sum);}int main(){ int i,j; int b; int u,v,w; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) scanf("%d",&map[i][j]); } int m; scanf("%d",&m); for(i=0;i<m;i++) { scanf("%d %d",&u,&v); map[u][v]=map[v][u]=0; } prim(); } return 0;}
zoj 1586
题意:
给出几个路由器,要求用最短的花费把所有的路由器连到一起,使其可以连通。每连通两个路由器的花费为,两个路由器的花费+路径的花费
分析:
这个题当时没转过来弯,没想通用Prim如何处理花费问题,因为prim计算最小生成树是找不到两个端点的,只记录当前树与其它点的最短路。这个题目,把两端的花费加到了路径的花费上,存到了map中。剩下的就是模板了。
AC代码:
#include <stdio.h>#include <string.h>#define INF 1<<30int map[1005][1005];int a[1005];int dis[1005];int vis[1005];int n;void prim(){ int i,j; int sum=0; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) dis[i]=map[0][i]; dis[0]=0; vis[0]=1; int mark,min; for(i=1;i<n;i++) { min=INF; for(j=0;j<n;j++) { if(!vis[j] && dis[j] < min) { min = dis[j]; mark = j; } } vis[mark]=1; sum+=dis[mark]; for(j=0;j<n;j++) { if(!vis[j] && dis[j] > map[mark][j]) dis[j] = map[mark][j]; } } printf("%d\n",sum);}int main(){ int T; int i,j; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",a+i); for(i=0;i<n;i++) { for(j=0;j<n;j++) { scanf("%d",map[i]+j); map[i][j]+=a[i]+a[j]; } } prim(); } return 0;}
题目链接
https://vjudge.net/contest/159179#overview
HDU 1285
题意:
给出n个队伍,m场比赛结果,判断这n个人的排名。有不同的排名结果则输出编号小的队伍在前
分析:
该题目拓扑排序就可以判断出排名,只是要求排名结果输出编号小的队伍在前。这里写了两种方法:
- 直接进行拓扑排序
- 用优先队列进行操作,实现拓扑排序
其中有一个有一个坑,就是同一个比赛结果可能输出多次,这里需要判断一下,再次输入的时候,不再加入度。
AC代码:
#include <stdio.h>#include <string.h>int map[505][505];int indegree[505];int n,m;void toposort(){ int i,j,k; for(i=0;i<n;i++) { for(j=1;j<=n;j++) { if(indegree[j]==0) { indegree[j]--; break; } } if(i==0) printf("%d",j); else printf(" %d",j); for(k=1;k<=n;k++) { if(map[j][k]==1) indegree[k]--; } } putchar('\n');}int main(){ int i,j; int a,b; while(scanf("%d %d",&n,&m)!=EOF) { memset(indegree,0,sizeof(indegree)); memset(map,0,sizeof(map)); for(i=0;i<m;i++) { scanf("%d %d",&a,&b); if(!map[a][b]) { map[a][b]=1; indegree[b]++; } } toposort(); } return 0;}
#include <stdio.h>#include <string.h>#include <queue>using namespace std;int map[505][505];int indegree[505];int p[505];int cnt=0;int n,m;struct node{ int x; bool operator < (const node &a) const{ return a.x < x; }};priority_queue <node>q;void toposort(){ int i,j; for(i=1;i<=n;i++) { if(indegree[i]==0) { node temp; temp.x = i; q.push(temp); } } while(!q.empty()) { node top = q.top(); p[cnt++] = top.x; q.pop(); for(i=1;i<=n;i++) { if(map[top.x][i]==1 && --indegree[i]==0) { node temp; temp.x = i; q.push(temp); } } }}int main(){ int a,b; int i,j; while(scanf("%d %d",&n,&m)!=EOF) { memset(indegree,0,sizeof(indegree)); memset(map,0,sizeof(map)); memset(p,0,sizeof(p)); for(i=0;i<m;i++) { scanf("%d %d",&a,&b); if(!map[a][b]) { map[a][b]=1; indegree[b]++; } } cnt=0; toposort(); printf("%d",p[0]); for(i=1;i<cnt;i++) printf(" %d",p[i]); putchar('\n'); } return 0;}
- 第二周---最小生成树(Kruskal,Prim)、拓扑排序
- 图论 邻接链表存储 BFS DFS 拓扑排序 最小生成树 KRUSKAL PRIM
- 最小生成树算法(prim&kruskal)
- 最小生成树(Kruskal+Prim)
- 最小生成树(MST):Prim / Kruskal
- 最小生成树(prim&kruskal)模板
- 最小生成树[Kruskal&&Prim](学习)
- 最小生成树(prim和kruskal)
- 最小生成树(Prim与Kruskal)
- Prim、Kruskal最小生成树
- 最小生成树---Prim---Kruskal
- 最小生成树 Kruskal&&Prim
- 最小生成树 Prim Kruskal
- Kruskal/prim--最小生成树
- 最小生成树 PRIM KRUSKAL
- 数据结构 学习笔记(九):图(下):最小生成树(Prim,Kruskal 算法),拓扑排序 AOV,关键路径 AOE
- 图的最小生成树原理PRIM、Kruskal(一)
- 【算法复习】图的最小生成树(Prim&Kruskal)
- es6学习笔记2
- android关于SlidingDrawer的那些坑
- 备考(京东2016实习生真题)
- 【ZOJ 3961 Let's Chat】
- rabbitmq——用户管理
- 第二周---最小生成树(Kruskal,Prim)、拓扑排序
- IntelliJ IDEA 2016.1.1(64) 长时间激活教程(到2099年)
- Servlet 文件上传
- 机器学习-实战-入门-javaml-KNN处理iris
- framework 编译
- 寄存器
- 【译】Vue框架引入JS库的正确姿势
- error: warning: Stopped in a context claiming to capture an Objective-C object pointer,
- Earth GMT Materials