P1330.封锁阳光大学

这几天没有更新,给大家说声抱歉啦……你们的小酱仍然在进步,不会辜负大家的期望哒~
今天我们来做一道福建夏令营的题目,也算是继续学习信奥啦…也算是和@十一维的智子继续干架 继续学习。
题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

阳光大学的校园是一张由 nn 个点构成的无向图,nn 个点之间由 mm 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

输入格式
第一行两个正整数,表示节点数和边数。接下来 mm 行,每行两个整数 u,vu,v表示点 uu 到点 vv 之间有道路相连。

输出格式
仅一行如果河蟹无法封锁所有道路,则输出 Impossible,否则输出一个整数,表示最少需要多少只河蟹。

输入输出样例
输入 #1

3 3
1 2
1 3
2 3
输出 #1

Impossible
输入 #2

3 2
1 2
2 3
输出 #2

1
说明/提示
【数据规模】
对于 100% 数据,1<=n<=104,1<=m<=105,保证没有重边。

首先,这都有疫情了你还遛个啥呀遛…是吧,所以小矿我先给河蟹点赞。

不过说回正题,这一题是一个好题。如果知道思路了,便会非常简单。但是如果不知道思路,却比较的难想出来。

我们来分析一下题目。首先,肯定要明确一点,那就是这个图是不一定联通的。于是,我们就可以将整张图切分成许多分开的连同子图来处理。然而最重要的事情是:如何处理一个连通图?

乍看下去,似乎无从下手,因为方案好像有很多种,根本就枚举不完。但是,关键要注意到题目中重要的两个条件,我们把它抽象成这两个要素:

①每一条边所连接的点中,至少要有一个被选中。②每一条边所连接的两个点,不能被同时选中。由此,可以推断出:
每一条边都有且仅有一个被它所连接的点被选中。
又因为我们要处理的是一个连通图。所以,对于这一个图的点的选法,可以考虑到相邻的点染成不同的颜色。

于是,对于一个连通图,要不就只有两种选法(因为可以全部选染成一种色的,也可以全部选染成另一种色的),要不就是impossible!
所以,我们只需要找到每一个子连通图,对它进行黑白染色,然后取两种染色中的最小值,然后最后汇总,就可以了。

另外,要判断impossible,只需要加一个used数组,记录已经遍历了哪些点。如果重复遍历一个点,且与上一次的颜色不同,则必然是impossible的。

所以我们还是看一下代码吧…

#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
#include<string>
#include<algorithm>
using namespace std;
struct Edge
{
    int t;
    int nexty;
}edge[200000];
int head[20000];
int cnt=0;
void add(int a,int b)
{
    cnt++;
    edge[cnt].t=b;
    edge[cnt].nexty=head[a];
    head[a]=cnt;
}
bool used[20000]={0};
int col[20000]={0};
int sum[2];
bool dfs(int node,int color)
{
    if(used[node])
    {
        if(col[node]==color)
    {
      return true;
    }
    return false;
  }
    used[node]=true;
    sum[col[node]=color]++;
    bool tf=true;
    for(int i=head[node];i!=0&&tf;i=edge[i].nexty)
    {
        tf=tf&&dfs(edge[i].t,1-color);
    }
    return tf;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int a,b;
    while(m--)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(used[i])
    {
        continue;
    } 
        sum[0]=sum[1]=0;
        if(!dfs(i,0))
        {
            printf("Impossible");
            return 0;
        }
        ans=ans+min(sum[0],sum[1]);
    }
    printf("%d",ans);
    return 0;
}

还好吧,只要学过了图,应该就没问题了,只是要思考个…两三个小时吧。

在此,小酱向大家致歉!由于过年,连续几周没有更了,这对大家的期望及关注都是极大的消耗!因此,我们明天继续更新…
另:请大家继续关注@十一维的智子,有消息第一时间在留言板上留言,谢谢哟~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值