其他分享
首页 > 其他分享> > 题解 luogu1330 封锁阳光大学

题解 luogu1330 封锁阳光大学

作者:互联网

考虑到题中要覆盖到所有的边,则深搜+染色不失为一种好方法

认认真真(欢欢喜喜)的写了一个深搜准备A了这道题,结果悲剧发生了——

这图不一定是联通图QAQ。

于是我光荣地炸掉了,不过感谢上苍我还有50分……

其实解决方法很简单,深搜模块可以不变(但要加上联通块的染色),
在主模块中套一个循环枚举边,加一个染色数组标记联通块,已染过
的联通块便不必再深搜

接下来就是主角——深搜

我的深搜只搜要放河蟹的点

放了河蟹的点标记为 1,
而与它相连的不能放河蟹的点标记为 2 ,

若出现矛盾,则此方案不行,可以返回
一个极大值,再从可行方案中选出河蟹最少的一种

注意到每一个边都要被覆盖,且只能在一个端点放河蟹,那么只要确定了一个点是否放河蟹,那么覆盖整个联通块的方案也就随之确定,

所以每一个联通块放河蟹的方法只有两种

即i点放河蟹和i点不放河蟹(其中i点是联通块中的任意一点)

又因为不能在两个端点同时放河蟹,所以联通块的方案又可以是v放河蟹与u放河蟹(u,v是联通块中任意一边的起点与终点);

因为我的深搜是只搜放河蟹的点,所以在主模块中只用dfs(u),dfs(v),再把两种方案所需的河蟹数相比较 ,选出最小值加入ans中,就像这样:

        
        cnt=0; mem(vis1);//记得初始化 
        //cnt记录该方案的河蟹数
        dfs(u);//假设起点放河蟹 
        int x=cnt;
        
        cnt=0;mem(vis1);
        dfs(v);//假设终点放河蟹 
        int y=cnt;
        
        
        if(x==1000000&&y==1000000)
        {//如果两种方法都不能覆盖所有的边,直接输出不可行 
            printf("Impossible");
            return;
        }
        ans+=Min(x,y);//选择两个方案中最小的 

在深搜模块中,加一个双重循环,

第一层枚举与u(也就是放了河蟹的点)相连的边的另一端,标记为2,若其中有已经被标记为1的,则矛盾,返回极大值

第二层枚举与不能放河蟹的v点相连的点(u除外),这些点必须放河蟹,(因为v不放河蟹),进行
下一轮深搜 若已被标记为1 则跳过,若被标记为2,则与它必须放河蟹矛盾,返回极大值

for(int i=first[u];i;i=edge[i].nt)
//枚举与u相连的不能放河蟹的点 
    {
        int v=edge[i].v;
        if(vis1[v]==1) {cnt=1000000;return 0;}
        //如果终点也放了河蟹,则这种方案是不可行的 
        vis1[v]=2;
        //标记终点未放河蟹但不能再放河蟹 
        for(int j=first[v];j;j=edge[j].nt)
        //枚举下一层要放河蟹的点 
        {
            int v1=edge[j].v;
            //因为v不能放河蟹,则v1必须放河蟹 
            if(vis1[v1]==1) continue;
            //已经有一个端点有了河蟹 
            if(vis1[v1]==2) {cnt=1000000;return 0;}
            //两个都不能再放河蟹,则这条边不能被覆盖,该方案不可行 
            if(!dfs(v1)) return 0;//若该方案不行 
        }
    }

还有细节详见代码注释~

/*
User:Mandy.H.Y
language:c++
Problem:1330
深搜,广搜,并查集都行 
*/
#include<bits/stdc++.h>
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define mem(A) memset((A),0,sizeof(A))

using namespace std;

const int maxn=10005;
const int maxm=100005;

int n,m,size=0,ans,cnt;
int first[maxn];
int vis[maxn],vis1[maxn];
//vis用于标记联通块
//vis1用于深搜染色 
struct Edge
{
    int u,v,nt;//存入起点的目的是为了标记联通块和深搜
}edge[maxm<<1];

template<typename T>inline void read(T &x)
{
    x=0;char c=getchar();bool f=0;
    while(c<'0'||c>'9') {f|=(c=='-');c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    if(f) x=-x;
}//读入优化 

template<typename T>void putch(const T x)
{
    if(x>9) putch(x/10);
    putchar((x%10)|48);
}

template<typename T>inline void put(const T x)
{
    if(x<0) putchar('-'),putch(-x);
    else putch(x);
}//输出优化 

void docu()
{
    freopen("1330.txt","r",stdin);
}

void eadd(int u,int v)
{
    edge[++size].v=v;
    edge[size].u=u;
    edge[size].nt=first[u];
    first[u]=size;
}//链表存边 

void readdata()//读入数据 
{
    read(n);read(m);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        read(x);read(y);
        eadd(x,y);
        eadd(y,x);
    }
}

bool dfs(int u)//我只搜要放河蟹的点 
{
    vis[u]=1;//标记联通块 
    vis1[u]=1;//标记已放了河蟹 
    ++cnt;//cnt是已放河蟹的个数 
    for(int i=first[u];i;i=edge[i].nt)//枚举与u相连的不能放河蟹的点 
    {
        int v=edge[i].v;
        if(vis1[v]==1) {cnt=1000000;return 0;}//如果终点也放了河蟹,则这种方案是不可行的 
        vis1[v]=2;//标记终点未放河蟹但不能再放河蟹 
        for(int j=first[v];j;j=edge[j].nt)//枚举下一层要放河蟹的点 
        {
            int v1=edge[j].v;//因为v不能放河蟹,则v1必须放河蟹 
            if(vis1[v1]==1) continue;//已经有一个端点有了河蟹 
            if(vis1[v1]==2) {cnt=1000000;return 0;}//两个都不能再放河蟹,则这条边不能被覆盖,该方案不可行 
            if(!dfs(v1)) return 0;//若该方案不行 
        }
    }
    return 1;//若没有冲突 ,则此方案可行 
}

void work()
{
    ans=0;
    //注意,图不一定联通 ,所以要枚举边来标记联通块,这与我的深搜有关 
    for(int i=1;i<=(m<<1);i+=2)//枚举边,因为存的是无向边,所以m<<1(相当于m*2),
     //又因为同一条边是连续存的两次故i+=2 
    {
        int v=edge[i].v;
        int u=edge[i].u;
        if(vis[u]||vis[v]) continue;//如果已经遍历过这个联通块 
        
        cnt=0; mem(vis1);//记得初始化 
        dfs(u);//假设起点放河蟹 
        int x=cnt;
        
        cnt=0;mem(vis1);
        dfs(v);//假设终点放河蟹 
        int y=cnt;
        
        if(x==1000000&&y==1000000)
        {//如果两种方法都不能覆盖所有的边,直接输出不可行 
            printf("Impossible");
            return;
        }
        ans+=Min(x,y);//选择两个方案中最小的 
    }
    put(ans);
}

int main()
{
//  docu();
    readdata();//模块化虽然程序稍显冗长,但是调试很方便
    work();
    return 0;
}

标签:封锁,河蟹,vis1,联通,标记,int,题解,cnt,luogu1330
来源: https://www.cnblogs.com/Mandy-H-Y/p/11354037.html