• luogu1330 封锁阳光大学


    题目

    题目描述
    曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

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

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

    输入格式
    第一行:两个整数N,M

    接下来M行:每行两个整数A,B,表示点A到点B之间有道路相连。

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

    输入输出样例
    输入 #1 复制
    3 3
    1 2
    1 3
    2 3
    输出 #1 复制
    Impossible
    输入 #2 复制
    3 2
    1 2
    2 3
    输出 #2 复制
    1
    说明/提示
    【数据规模】

    1<=N<=10000,1<=M<=100000,任意两点之间最多有一条道路。

    分析

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

    认认真真(欢欢喜喜)的写了一个深搜准备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;
    }
    
    非做顽石不可,哪管他敬仰暗唾
  • 相关阅读:
    抽象工厂学习笔记
    SQL Transcation的一些总结
    享元模式(Flyweight)
    编程语言简史
    代理模式(Proxy)
    打造属于你的提供者(Provider = Strategy + Factory Method)
    打造属于你的加密Helper类
    单例模式(Singleton)的6种实现
    在C#中应用哈希表(Hashtable)
    InstallShield脚本语言的编写
  • 原文地址:https://www.cnblogs.com/Mandy-H-Y/p/11354037.html
Copyright © 2020-2023  润新知