第二周---最小生成树(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;}
0 0
原创粉丝点击