图论练习

这几天和潘学长 达成了一起刷题的协议,然后潘学长带着我们想图论开始发起进攻(  冲鸭ヾ(≧O≦)〃嗷~)

A - How Many Tables  

戳这戳这~http://acm.hdu.edu.cn/showproblem.php?pid=1213

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers. 

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table. 

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least. 

Input

The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.

Output

For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks. 

Sample Input

2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output

2
4

 

红果果的并查集(红果果真好次

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
using namespace std;

int  pre[1050];
bool t[1050];

int Find(int x)
{
    int r=x;
    while(r!=pre[r])
        r=pre[r];

    int i=x,j;
    while(pre[i]!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}

void mix(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)
    {
        pre[fy]=fx;
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int N,M;
        scanf("%d%d",&N,&M);
	    for(int i=1;i<=N;i++)
            pre[i]=i;

		int a,b;
        for(int i=1;i<=M;i++)
        {
            scanf("%d%d",&a,&b);
            mix(a,b);
        }


        memset(t,0,sizeof(t));
        for(int i=1;i<=N;i++)
        {
            t[Find(i)]=1;
        }
        int ans=0;
        for(int i=1;i<=N;i++)
            if(t[i])
                ans++;

        printf("%d\n",ans);

    }
    return 0;
}

B - 小希的迷宫

戳这戳这~http://acm.hdu.edu.cn/showproblem.php?pid=1272

上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。 

Input

输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。 
整个文件以两个-1结尾。 

Output

对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。 

Sample Input

6 8  5 3  5 2  6 4
5 6  0 0

8 1  7 3  6 2  8 9  7 5
7 4  7 8  7 6  0 0

3 8  6 8  6 4
5 3  5 6  5 2  0 0

-1 -1

Sample Output

Yes
Yes
No

判断有没有环和集合个数,如果有环的话就不能构成,图应该是一棵树,如果集合大于1,也不能构成

即利用并查集的Find 如果fx==fy就有环了,也可以利用树的顶点和边的关系edge=vritix-1

1.并查集


#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
using namespace std;

int  pre[100000+5];//根 判断集合个数
bool vis[100000+5];//是否访问 判断集合个数  
bool flag;//判断是否存在环

int Find(int x)
{
    int r=x;
    while(r!=pre[r])
        r=pre[r];

    return r;
}

bool mix(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)
    {
        pre[fy]=fx;
        return false;
    }
    return true;//判断是否存在环
}

void Init()
{
    flag=0;
    for(int i=1;i<=100000;i++)
    {
        vis[i]=false;
        pre[i]=i;
    }
}

int main()
{
    int N,M;
    while(scanf("%d%d",&N,&M)&&N!=-1&&M!=-1)
    {
        vis[N]=vis[M]=true;
        if(N==0&&M==0)
        {
            if(flag)
                cout<<"No"<<endl;
            else
            {
                int sum=0;//集合的个数,判断所有点是否在同一集合内
                for(int i=0;i<=100000;i++)
                {
                    if(vis[i]&&pre[i]==i)
                        sum++;
                    if(sum>1)
                        break;
                }
                if(sum>1)
                    cout<<"No"<<endl;
                else
                    cout<<"Yes"<<endl;
            }
            Init();//0 0是一个图的结束 也代表另一个图将要开启 进行初始化
            continue;
        }

        if(mix(N,M))//判断是否存在环
            flag=1;
    }
    return 0;
}

2.利用树的边=顶点-1

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define ll long long
const int maxn=100000+5;
int a[maxn];
int e,v;//边 顶点

void have(int x)
{
    if(!a[x])
    {
        a[x]=1;
        v++;//顶点数
    }
}

int main()
{

    int x,y;
    while(cin>>x>>y)
    {
        if(x==-1&&y==-1)
            break;

        else if(x==0&&y==0)
        {
            if(e==v-1||(e==0&&v==0))//顶点和边关系
               cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;

            e=0,v=0;
            memset(a,0,sizeof a);
        }
        else
        {
            have(x),have(y);
            e++;
        }
    }
}

C - find the most comfortable road

戳这戳这~http://acm.hdu.edu.cn/showproblem.php?pid=1596

XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Speed,同时XX星人对 Flycar的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为SARS的限速要求,flycar必须瞬间提速/降速,痛苦呀 ), 
但XX星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(SARS是双向的)。 

Input

输入包括多个测试实例,每个实例包括: 
第一行有2个正整数n (1<n<=200)和m (m<=1000),表示有N个城市和M条SARS。 
接下来的行是三个正整数StartCity,EndCity,speed,表示从表面上看StartCity到EndCity,限速为speedSARS。speed<=1000000 
然后是一个正整数Q(Q<11),表示寻路的个数。 
接下来Q行每行有2个正整数Start,End, 表示寻路的起终点。

Output

每个寻路要求打印一行,仅输出一个非负整数表示最佳路线的舒适度最高速与最低速的差。如果起点和终点不能到达,那么输出-1。

Sample Input

4 4
1 2 2
2 3 4
1 4 1
3 4 2
2
1 3
1 2

Sample Output

1
0

从在所有a到b的路径中,找速度差最小的,并查集扫一遍,判断是否可以到达

利用排序预处理 便于计算速度差

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

int pre[205];
struct Node
{
    int s,e,d;
};
Node node[1005];
int cmp(Node a,Node b)
{
    return a.d<b.d;
}
int Find(int x)
{
    int r=x;
    while(r!=pre[r])
		r=pre[r];

    return r;
}

void mix(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)
        pre[fy]=fx;
}


int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        for(int i=0;i<m;i++)
        {
           cin>>node[i].s>>node[i].e>>node[i].d;
        }
        sort(node,node+m,cmp);//速度从小到大排序,便于后面计算速度差 
        int t;
        cin>>t;
        while(t--)
        {
            int ans=INF;
            int s,e;
            cin>>s>>e;
            for(int j=0;j<m;j++)
            {
                for(int i=1;i<=n;i++)
                    pre[i]=i;
                for(int i=j;i<m;i++)
                {
                    int u=node[i].s,v=node[i].e,d=node[i].d;
                    mix(u,v);
                    if(Find(s)==Find(e))//如果从star到end 有路
                    {
                        ans=min(ans,d-node[j].d);//小的速度差
                        break;
                    }
                }
            }
        if(ans==INF)
            cout<<-1<<endl;
        else
            cout<<ans<<endl;
        }
    }
    return 0;
}

D - Hand in Hand

戳这戳这~http://acm.hdu.edu.cn/showproblem.php?pid=3926

In order to get rid of Conan, Kaitou KID disguises himself as a teacher in the kindergarten. He knows kids love games and works out a new game called "hand in hand". 

Initially kids run on the playground randomly. When Kid says "stop", kids catch others' hands immediately. One hand can catch any other hand randomly. It's weird to have more than two hands get together so one hand grabs at most one other hand. After kids stop moving they form a graph. 

Everybody takes a look at the graph and repeat the above steps again to form another graph. Now Kid has a question for his kids: "Are the two graph isomorphism?" 

Input

The first line contains a single positive integer T( T <= 100 ), indicating the number of datasets. 
There are two graphs in each case, for each graph: 
first line contains N( 1 <= N <= 10^4 ) and M indicating the number of kids and connections. 
the next M lines each have two integers u and v indicating kid u and v are "hand in hand". 
You can assume each kid only has two hands. 

Output

For each test case: output the case number as shown and "YES" if the two graph are isomorphism or "NO" otherwise. 

Sample Input

2

3 2
1 2
2 3
3 2
3 2
2 1

3 3
1 2
2 3
3 1
3 1
1 2

Sample Output

Case #1: YES
Case #2: NO

同构图,判断是不是同构图,就看每个点的度数和环数 是否相同。(以后知道同构图到底是神马东东惹,嘤嘤嘤(╥╯^╰╥))用并查集把每个点的度数和环数算出来然后记录一下。

注意把a点连在b点的时候,就是两个集合合并的时候)

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int pre[maxn];
struct node
{
    int u,c;//度 环
}s1[maxn],s2[maxn];

bool cmp(node a,node b)
{
    if(a.u==b.u)
        return a.c>b.c;
    else
        return a.u>b.u;
}

void Init(int n)
{
    for(int i=1;i<=n;i++)
        pre[i]=i;
}

int Find(int x)
{
    int r=x;
    while(pre[r]!=r)
        r=pre[r];

    return r;
}

void mix(int x,int y,int k)
{
    if(k==1)
    {
        int dx=Find(x);
        int dy=Find(y);
        if(dx!=dy)
        {
            pre[dx]=dy;
            s1[dy].u+=s1[dx].u;//集合合并 把拉手的孩子数量加起来,下同
            s1[dx].u=0;//被合并 归零
        }
        else
            s1[dy].c++;//成环
    }
    else
    {
        int dx=Find(x),dy=Find(y);
        if(dx!=dy)
        {
            pre[dx]=dy;
            s2[dy].u+=s2[dx].u;
            s2[dx].u=0;
        }
        else
            s2[dy].c++;
    }

}

int main()
{
    int t;
    scanf("%d",&t);
    int kase=0;
    while(t--)
    {
        for(int i=1;i<10010;i++)
        {
            s1[i].u=1;s1[i].c=0;
            s2[i].u=1;s2[i].c=0;//最开始每个都是独立的,默认为链=0
        }
        bool flag=false;
        int n1,m1,n2,m2;
        scanf("%d%d",&n1,&m1);
        Init(n1);
        for(int i=0;i<m1;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int dx=Find(a);
            int dy=Find(b);
            mix(a,b,1);
        }

        scanf("%d%d",&n2,&m2);
        Init(n2);
        for(int i=0;i<m2;i++)
        {
            int c,d;
            scanf("%d%d",&c,&d);
            mix(c,d,2);
        }
        if(n1==n2)
        {
            sort(s1+1,s1+n1+1,cmp);
            sort(s2+1,s2+n2+1,cmp);//排序,若孩子的数量相同则对是否是环进行排序,这里要注意

            for(int i=0;i<n1;i++)
            {
                if(s1[i].u!=s2[i].u||s1[i].c!=s2[i].c)
                {//判断数量,状态
                    flag=true;
                    break;
                }
            }
        }
        if(n1!=n2)
            flag=true;

        if(flag)
            printf("Case #%d: NO\n",++kase);
        else
            printf("Case #%d: YES\n",++kase);
    }
    return 0;
}

E - Portal

戳这戳这~http://acm.hdu.edu.cn/showproblem.php?pid=3938

ZLGG found a magic theory that the bigger banana the bigger banana peel .This important theory can help him make a portal in our universal. Unfortunately, making a pair of portals will cost min{T} energies. T in a path between point V and point U is the length of the longest edge in the path. There may be lots of paths between two points. Now ZLGG owned L energies and he want to know how many kind of path he could make.

Input

There are multiple test cases. The first line of input contains three integer N, M and Q (1 < N ≤ 10,000, 0 < M ≤ 50,000, 0 < Q ≤ 10,000). N is the number of points, M is the number of edges and Q is the number of queries. Each of the next M lines contains three integers a, b, and c (1 ≤ a, b ≤ N, 0 ≤ c ≤ 10^8) describing an edge connecting the point a and b with cost c. Each of the following Q lines contain a single integer L (0 ≤ L ≤ 10^8).

Output

Output the answer to each query on a separate line.

Sample Input

10 10 10
7 2 1
6 8 3
4 5 8
5 8 2
2 8 9
6 4 5
2 1 5
8 10 5
7 3 7
7 8 8
10
6
1
5
9
1
8

离线的并查集,由于查询次数太多,如果查询一次用一次并查集肯定会超时,那里就从最小的开始把全部都算出来。为什么要从最小的开始呢,题目定义两个节点间的距离为两个节点所有路径上经过的点中任意相邻两点的边最长的那一对点的边的权值,
将每个点初始化为一个集合,然后按照边的大小依次将点对进行合并,这样能保证每次当前合并的边是所有已经合并的边里最长的,即已经形成的集合里任意两点间边长不大于当前正在合并的边长,那么就可以用当前合并的边长与要求中的L值进行比较选择合并之后是否能用,这里num记录的是集合内点的个数,那么当两个集合合并时,新增加的路径的条数就是num[a]*num[b],再加上之前合并完成的边的条数,既是当前解。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=50000+5;
int n,m,q;
int pre[maxn],num[maxn];
struct Edge
{
    int u,v,w;
    bool operator < (const Edge tmp) const
    {
        return w<tmp.w;
    }
}edge[maxn];

struct Node
{
    int id,l;
    bool operator < (const Node tmp) const
    {
        return l<tmp.l;
    }
}node[maxn];

int Find(int x)
{
    int r=x;
    while(pre[r]!=r)
        r=pre[r];

    return r;
}

int Merge(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx==fy)
        return 0;  //在集合中,已经计算过了
    pre[fx]=fy;
    int ans=num[fx]*num[fy];
    num[fy]+=num[fx];//合并为一个集合 两个集合里面的点合成
    num[fx]=0;//被合并的集合归零
    return ans;
}
int main()
{
    int ans[maxn];
    while(scanf("%d%d%d",&n,&m,&q)!=EOF)
    {
        memset(ans,0,sizeof ans);
        for(int i=0;i<m;i++)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        for(int i=0;i<q;i++)
        {
            scanf("%d",&node[i].l);
            node[i].id=i;
        }
        sort(edge,edge+m);
        sort(node,node+q);
        for(int i=0;i<=n;i++)
            num[i]=1,pre[i]=i;
        int pos=0,tmp=0;
        for(int i=0;i<q;i++)
        {
            while(pos<m&&node[i].l>=edge[pos].w)
            {
                tmp+=Merge(edge[pos].u,edge[pos].v);
                pos++;
            }
            ans[node[i].id]=tmp;
        }
        for(int i=0;i<q;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值