HFOI2017.7.15校内赛(普及组)题解

T1:More is better
Problem Description
Mr Wang wants some boys to help him with a project. Because the project is rather complex, the more boys come, the better it will be. Of course there are certain requirements.
Mr Wang selected a room big enough to hold the boys. The boy who are not been chosen has to leave the room immediately. There are 10000000 boys in the room numbered from 1 to 10000000 at the very beginning. After Mr Wang's selection any two of them who are still in this room should be friends (direct or indirect), or there is only one boy left. Given all the direct friend-pairs, you should decide the best way.
Input
The first line of the input contains an integer n (0 ≤ n ≤ 100 000) - the number of direct friend-pairs. The following n lines each contains a pair of numbers A and B separated by a single space that suggests A and B are direct friends. (A ≠ B, 1 ≤ A, B ≤ 10000000)
Output
The output in one line contains exactly one integer equals to the maximum number of boys Mr Wang may keep.
Sample Input
4
1 2
3 4
5 6
1 6
4
1 2
3 4
5 6
7 8
Sample Output
4
2
Hint
A and B are friends(direct or indirect), B and C are friends(direct or indirect),
then A and C are also friends(indirect).
In the first sample {1,2,5,6} is the result.
In the second sample {1,2},{3,4},{5,6},{7,8} are four kinds of answers.
经典的并查集问题,开一个数组附加当前该朋友系列(树)的节点数,合并的时候相加即可。注意压缩路径。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000010;
int n,x,y;
int f[maxn],maxx;
int a[maxn];
int fa(int x)
{
    if(f[x]==0)return x;
    else
    {
        f[x]=fa(f[x]);
        return f[x];
    }
}
void Merge(int a,int b)
{
    int x=fa(a),y=fa(b);
    if(x!=y){f[y]=x;}
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        maxx=-1;
        memset(f,0,sizeof f);
        memset(a,0,sizeof a);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            Merge(x,y);
        }
        for(int i=1;i<maxn;i++)a[fa(i)]++;
        for(int i=1;i<maxn;i++)maxx=max(maxx,a[i]);
        printf("%d\n",maxx);
    }
    return 0;
}
T2:继续畅通工程
Problem Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建
当N为0时输入结束。
Output
每个测试用例的输出占一行,输出全省畅通需要的最低成本。
Sample Input
3
1 2 1 0
1 3 2 0
2 3 4 0
3
1 2 1 0
1 3 2 0
2 3 4 1
3
1 2 1 0
1 3 2 1
2 3 4 1
0
Sample Output
3
1
0
T3:Constructing Roads
Problem Description
There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each other. We say two village A and B are connected, if and only if there is a road between A and B, or there exists a village C such that there is a road between A and C, and C and B are connected.
We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
Input
The first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.
Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
Output
You should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum.
Sample Input
3
0 990 692
990 0 179
692 179 0
1
1 2
Sample Output
179
2,3题都是有关最小生成树的题目,我分别用了Kruskal和Prim算法。
Kruskal算法思想:每次中间过程返回的是一个森林。将每条边排序,每次插入长度最小且插入后不会产生环的一条边。为了方便插入边和判环,需要使用并查集。适用于稀疏图。
Prim算法思想:每次中间过程返回的是一棵树。从任意一个结点开始,每次选取离当前树距离最近的结点,将他们之间的边插入。为了方便选取离树距离最近的结点,需要使用优先队列优化。
第二题代码:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
int pre[maxn];
int n,m;
struct node{int f,t,c;}s[maxn];
int cmp(node a,node b){return a.c<b.c;}
int find(int x)
{
    if(pre[x]!=x)pre[x]=find(pre[x]);
    return pre[x];
}
int merge(node a)
{
    int x=find(a.f);
    int y=find(a.t);
    if(x!=y)
    {
        pre[x]=y;
        return a.c;
    }
    return 0;
}
int kruskal()
{
    int ans=0;
    for(int i=0;i<m;i++)ans+=merge(s[i]);
    return ans;
}
int main()
{
    while(scanf("%d",&n)&&n!=0)
    {
        int x;
          for(int i=1;i<=n;i++)pre[i]=i;
        m=n*(n-1)/2;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d%d",&s[i].f,&s[i].t,&s[i].c,&x);
            if(x)s[i].c=0;
        }
        sort(s,s+m,cmp);
        printf("%d\n",kruskal());
    }
    return 0;
}
第三题代码(未加优先队列优化,31ms):
#include<stdio.h>
#include<string.h>
const int INF=0x3f3f3f;
const int inf=2e9;
const int maxn=110;
int lowcost[maxn],tf[maxn],map[maxn][maxn],city;
int prim()
{
    int next,min,mincost=0;
    memset(tf,0,sizeof tf);
    for(int i=1;i<=city;i++)lowcost[i]=map[1][i];
    tf[1]=1;
    for(int i=1;i<city;i++)
    {
        min=inf;
        for(int j=1;j<=city;j++)
        if(!tf[j]&&min>lowcost[j])
        {
            min=lowcost[j];
            next=j;
        }
        mincost+=min;
        tf[next]=1;
        for(int j=1;j<=city;j++)if(!tf[j]&&lowcost[j]>map[next][j])lowcost[j]=map[next][j];
    }
    return mincost;
}
int main()
{
    int i,j,road,x,y;
    int need;
    while(scanf("%d",&city)!=EOF)
    {
        memset(map,INF,sizeof map);
        for(i=1;i<=city;i++)
        {
            for(j=1;j<=city;j++)
            {
                scanf("%d",&map[i][j]);
            }
        }
        scanf("%d",&road);
        while(road--)
        {
            scanf("%d%d",&x,&y);
            map[x][y]=map[y][x]=0;
        }
        need=prim();
        printf("%d\n",need);
    } 
    return 0;
}
优先队列优化后的代码(15ms):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 111;
const int Inf = 1<<30;
int Map[MAXN][MAXN],Vis[MAXN],Dist[MAXN],Ans;
int N,Q;
int x,y;
void Initial()
{
    Ans = 0;
    fill(Vis,Vis + MAXN,0);
    fill(Dist,Dist + MAXN,Inf);
}
struct Node{
    int v,len;//v是编号,len是v到目标集合的距离(将所有已经连在一起的村庄看成一个集合)
    bool friend operator <(Node x,Node y)//用优先队列取出距离 已经连好的村庄 最近的村庄编号 
    {
        return x.len > y.len;
    }
};
void Prim()
{
    priority_queue<Node>Edge;
    Node Now;
    Now.v = 1,Now.len = 0;//假设先从第一个村庄开始,从其他的编号开始也可以,但是 len一定是 0 
    Edge.push(Now);
    while(!Edge.empty())
    {
        Now = Edge.top();
        Edge.pop();
        if(Vis[Now.v])continue;//已经在集合里,不再访问 
        Vis[Now.v] = 1;//否则标记为访问
        Ans += Now.len;//同时把当前节点 v到目标集合的距离len加到答案里,优先队列里取出来的可以保证是最短的路
        for(int i = 1;i <= N;i++)
        {
            if(!Vis[i] && Map[Now.v][i] < Dist[i])//满足此条件则可以加入队列 
            {
                Node Next;
                Next.v = i;
                Next.len = Dist[i] = Map[Now.v][i];
                Edge.push(Next); 
            }
        }
    }
    printf("%d\n",Ans);
}
int main()
{
    while(~scanf("%d",&N))
    {
        Initial();
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
                scanf("%d",&Map[i][j]);
        scanf("%d",&Q);
        while(Q--)
        {
            scanf("%d%d",&x,&y);//x,y已经相连 
            Map[x][y]=Map[y][x]=0;
        }
        Prim();
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值