zoukankan      html  css  js  c++  java
  • UVA-Knights of the Round Table La3523

    圆桌骑士;

    分析:如果两个骑士没仇, 那就连一条边, 这样得到一个图G, 那么问题就转化成了求G中的不在任何一个简单奇圈上的点的个数, 我们可以求出在的个数再做减

    又因为简单奇圈上的所有节点必定属于同一个双连通分量, 并且二分图没有奇圈, 所以问题就变成怎样判断一个双连通分量是否是奇圈见代码注释->

    *
    那么怎样判断一个双连通分量是奇圈呢?
    
    首先我们要接受两条定理,
    
    (1)如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),
    那么这个双连通分量的其他顶点也在某个奇圈中;
    
    (2)如果一个双连通分量含有奇圈,则他必定不是一个二分图。
    反过来也成立,这是一个充要条件。
    
    由于双连通分量也是一个图,那么要判断双连通分量是否为奇圈,只需判断这个双连通分量是否为一个二分图,
    而要判断一个图是否为二分图,就用交叉染色法!
    
    显然所有在奇圈中的骑士,都是允许出席会议的,而由于可能有部分骑士允许出席一个以上的会议(即他们是2个以上的奇圈的公共点),
    那么为了避免重复统计人数,
    当我们判断出哪些骑士允许出席会议时,就把他们做一个标记(相同的骑士只做一个标记)。
    最后当Tarjan算法结束后,我们统计一下被标记的人数有多少,再用总人数减去这部分人,剩下的就是被亚瑟王剔除的人数了。
    */
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define rep(i, j, k) for(int i=j; i<=k; ++i)
    #define N 1005
    #define mem(a, b) memset(a, b, sizeof(a))
    
    int n, m, pre[N], low[N], _, res, top, stack[N];
    int color[N], a[N];
    bool map[N][N], can[N], b[N];
    
    void init(){
    	mem(pre, -1);mem(can, false);mem(map, true);
    	_=res=top=0;
    }
    bool check(int u, int c){
    	color[u]=c;
    	rep(v, 1, n)	
    		if(map[u][v] && b[v]){
    			if(color[u]==color[v])return true;
    			if(color[v]==-1)
    				check(v, c^1);
    		}
    	return false;
    }
    void solve(int t, int *a){  
    	int i;
    	//cout<<t<<endl;
        memset(b,0,sizeof(b));  
        rep(j, 0, t-1){
            b[a[j]]=true;
    		//cout<<a[i]<<"**"<<endl;
    	}
        for(i=0; i<t; ++i){
            memset(color,-1,sizeof(color));  
            if(check(a[i], 1))  
                break;
        }  
        if(i<t)  
           rep(j, 0, t-1){  
                if(!can[a[j]]){  
                    res++;  
                    can[a[j]]=true;  
    				//cout<<res<<"****"<<endl;
                }  
            }
    }
    void dfs(int u){
    	low[u]=pre[u]=++_;
    	stack[top]=u;
    	top++;
    	//cout<<top<<endl;
    	rep(v, 1, n)
    		if(map[u][v]){
    			if(pre[v]==-1){
    				dfs(v);
    				low[u]=min(low[u],low[v]);
    				if(low[v]>=pre[u]){
    					int k=1;a[0]=u;
    					do{
    						a[k++]=stack[--top];
    					}while(stack[top]!=v);
    					solve(k, a);
    				}
    		}
    		else low[u]=min(low[u],pre[v]);
    	}
    }
    
    int main(){
    	while(1){
    		//read(n);read(m);
    		scanf("%d%d", &n, &m);
    		if((n+m)==0)break;
    		init();
    		rep(i, 1, m){
    			int u, v;
    			//read(u);read(v);
    			scanf("%d%d", &u, &v);
    			map[u][v]=map[v][u]=false;
    		}
    		rep(i, 1, n)map[i][i]=false;
    		rep(i, 1, n)
    			if(pre[i]<0)
    				dfs(i);
    		printf("%d
    ", n-res);
    	}
    	return 0;
    }
    思路有了, 这题做起来其实也不是很复杂。
  • 相关阅读:
    网络编程的基础
    day31作业
    异常处理其他内容
    异常处理的使用
    常见的异常种类
    ansible条件使用--实践
    Ansible的循环
    Ansible的条件语句
    ansibleplaybook的使用
    ansible官方文档翻译之变量
  • 原文地址:https://www.cnblogs.com/pbvrvnq/p/8530172.html
Copyright © 2011-2022 走看看