洛谷 P1330 封锁阳光大学 链式前向星 dfs

题目链接:

https://www.luogu.com.cn/problem/P1330

参考博客:

https://www.luogu.com.cn/blog/Kesdiael3/solution-p1330

这个博客解释的很详细,建议复习的时候可以再看看

思路:

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

2:每一条边都有且仅有一个被它所连接的点被选中:对于这一个图的点的选法,可以考虑到相邻的点染成不同的颜色

3:于是,对于一个连通图,要不就只有两种选法,要不就是impossible!

4:所以,我们只需要找到每一个子连通图,对它进行黑白染色,然后取两种染色中的最小值,然后最后汇总,就可以了

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

#include <bits/stdc++.h>

using namespace std;
int n,m,a,b,cnt,head[20002],sum[2];//黑白两种染色各自的点数
bool used[20002]={0};//是否遍历过
int col[20002]={0};//每一个点的染色
struct Edge
{
    int t;
    int next;
}edge[200002];
//链式前向星存边
void add(int a,int b)
{
    cnt++;
    edge[cnt].t=b;
    edge[cnt].next=head[a];
    head[a]=cnt;
}
//染色(返回false即impossible)
bool dfs(int x,int color)
{
    if(used[x])//如果已被染过色
    {
        if(col[x]==color)return true;//如果仍是原来的颜色,即可行
        return false;//非原来的颜色,即产生了冲突,不可行
    }
    used[x]=true;//记录
    sum[col[x]=color]++;//这一种颜色的个数加1,且此点的颜色也记录下来
    bool tf=true;//是否可行
    for(int i=head[x];i!=0&&tf;i=edge[i].next)//遍历边
    {
        tf=tf&&dfs(edge[i].t,1-color);//是否可以继续染色
    }
    return tf;//返回是否完成染色
}

int main()
{
    ios::sync_with_stdio(0);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        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+=min(sum[0],sum[1]);//加上小的一个
    }
    printf("%d",ans);
    return 0;
}
发布了117 篇原创文章 · 获赞 37 · 访问量 6573

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/104202843