zoj 3811 Untrusted Patrol The 2014 ACM-ICPC Asia Mudanjiang Regional First Round C

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5343

大致题意:给定一张图,再给出一个顶点编号的序列,问是否在这张图上是否能按这个顺序依次访问这些顶点(题目描述的很复杂抽象出来就是这样的一个问题)。

大致思路:用并查集算法,先按题目条件建图,我们假设有放置传感器的顶点为k类顶点,没有放置传感器的顶点为o类顶点,我们将k类顶点按所给顺序存好,我们就能将原问题转化成另外一个问题,就是相邻的两个k点,是否能通过o点和之前出现过的k类相连接。

所以,我们将两两相连的o类顶点先合到一个集合中,再将第一个k点所连的所有顶点合并到一起,接下来处理剩下的每个k类顶点,首先将此顶点所连的所有非k类顶点加入到集合中,再判断这个k类顶点和它前一个k类顶点是否是联通的,如果不联通可以直接输出No,再将此k类顶点的相连的所有k类顶点合并在一起,再将此k类顶点和前一个k类顶点合并在一起,这样的循环操作~~~。

注意要判一下图是否是联通的,和L是否小于K。

图不联通和L小于K都是可以直接判“No”的

code:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>

using namespace std;
#define SIZE 1000
#define INF 2147483647
typedef long long LL;

const int MAX_V=100100;
const int MAX_E=400200;

struct edge
{
    int to,next;
}G[MAX_E];

int head[MAX_V],si;
int par[MAX_V],num[MAX_V];
bool vis[MAX_V];
int kk[MAX_V],N,M,K;

void add_edge(int s,int t)
{
    G[si].to=t;
    G[si].next=head[s];
    head[s]=si++;
}
void init(int n_)
{
    for(int i=0;i<=n_;i++){
        par[i]=i;
        num[i]=1;
    }
}
int Find(int x)
{
    if(par[x]==x) return x;
    return par[x]=Find(par[x]);
}
void unite(int x,int y)
{
    x=Find(x);
    y=Find(y);
    if(x==y) return ;
    if(num[x]<num[y]){
        par[x]=y;
        num[y]+=num[x];
    }
    else{
        par[y]=x;
        num[x]+=num[y];
    }
}
bool same(int x,int y)
{
    return Find(x)==Find(y);
}
void solve()
{
    init(N);
    for(int i=1;i<=N;i++){
        if(vis[i]) continue;
        for(int j=head[i];j!=-1;j=G[j].next){
            int mid=G[j].to;
            if(vis[mid]) continue;
            unite(i,mid);
        }
    }
    for(int i=head[kk[0]];i!=-1;i=G[i].next){
        int mid=G[i].to;
        unite(kk[0],mid);
    }
    for(int i=1;i<K;i++){
        for(int j=head[kk[i]];j!=-1;j=G[j].next){
            int mid=G[j].to;
            if(vis[mid]) continue;
            unite(kk[i],mid);
        }
        if(!same(kk[i],kk[i-1])){
            printf("No\n");
            return ;
        }
        unite(kk[i],kk[i-1]);
        for(int j=head[kk[i]];j!=-1;j=G[j].next){
            int mid=G[j].to;
            if(vis[mid]) unite(kk[i],mid);
        }
    }
    printf("Yes\n");

}
int main()
{
    int T,mid,s,t;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&N,&M,&K);
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        si=0;
        init(N);
        for(int i=0;i<K;i++) scanf("%d",&mid);
        for(int i=0;i<M;i++){
            scanf("%d%d",&s,&t);
            add_edge(s,t);
            add_edge(t,s);
            unite(s,t);
        }
        scanf("%d",&mid);
        for(int i=0;i<mid;i++){
            scanf("%d",&kk[i]);
            vis[kk[i]]=true;
        }
        if(mid<K){
            printf("No\n");
            continue;
        }
        if(num[Find(1)]!=N){
            printf("No\n");
            continue;
        }
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值