poj2942 Knights of the Round Table(点双联通分量+染色法)

题    目    传    送    门    在    这


 题目大意

有n个骑士,m对憎恨关系,每个憎恨的骑士不会坐在一起。

现在要将尽量多的奇数个人安排到一个圆桌上参加会议,将永远无法参加会议的骑士踢出骑士团。

求最少要的踢出多少骑士。

解题思路

我们将没有憎恨关系的两个骑士连一条边,即作原图的补图。

根据题意,题目就是求出最长的奇环,用n减去长度就是答案。

这里我们先构造两个引理。

引理1:两个骑士属于不同的点双联通分量,那么它们将永远无法一起参加会议。

证明:(反证法)

设两个骑士为x和y,分别属于点双联通分量c[x]和c[y](c[x] != c[y])

假设它们可以一起参加,即有一条同时包含x,y的奇环。

因为x和y在同一环中,所以无论删去哪个节点,都有x到y的路径。

而c[x]和c[y]中删掉一个点也是也是联通的,所以c[x]和c[y]组成的图也是点双联通分量。

所以假设不成立。

证毕。

引理2:一个点双联通分量中存在奇环,则该点双联通分量中任意两个点都被任意一个包含。

证明:

设x和y属于同一个点双联通分量且在同一个奇环中,z为该双联通分量中的任意一点。

那么x到y的路径加上x经过z到y的路径即为一个环。

因为一个奇数只能分成偶数+奇数,所以x到y的两条路径为一奇一偶。

无论x经过z到y的路径长为多少,所以一定有包含x,y和z的奇环。

证毕。

所以说,我们用tarjan跑出该图中的点双联通分量,如果这个点双联通中有奇环,那么这个点双联通分量中的骑士都不会被踢出,把他标记掉。

最后统计一下没有被标记的人数。

代码如下: 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 1005
#define MAXM 2000005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii; 
const int INF = 0x3f3f3f3f;
const int p = 10000007;
int lowbit(int x){ return x & -x; }
int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p; } return x; }

stack<int> sta;
vector<int> dcc[MAXN];
int cnt, num, tot, now;
int head[MAXN], nxt[MAXM], to[MAXM];
int dfn[MAXN], low[MAXN], c[MAXN], col[MAXN];
bool res, g[MAXN][MAXN], able[MAXN];

void init(){
    cnt = num = tot = 0;
    clr(head, -1);
    clr(g, 0);
    clr(dfn, 0);
    clr(low, 0);
    clr(able, 0);
    clr(c, 0);
}

void add(int u, int v){
    nxt[cnt] = head[u];
    head[u] = cnt;
    to[cnt] = v;
    cnt++;
}

void tarjan(int u, int root){
    dfn[u] = low[u] = ++num;
    sta.push(u);
    if(u == root && head[u] == 0){
        dcc[++tot].pb(u);
        return;
    }
    for(int e = head[u]; e != -1; e = nxt[e]){
        int v = to[e];
        if(!dfn[v]){
            tarjan(v, root);
            low[u] = min(low[u], low[v]);
            if(dfn[u] <= low[v]){
                tot++;
                while(1){
                    int t = sta.top();
                    dcc[tot].pb(t);
                    sta.pop();
                    if(t == v) break;
                }
                dcc[tot].pb(u);
            }
        }
        else low[u] = min(low[u], dfn[v]);
    }
}

void dfs(int u, int color){
    col[u] = color;
    for(int e = head[u]; e != -1; e = nxt[e]){
        int v = to[e];
        if(c[v] != now) continue;
        if(!col[v]) dfs(v, 3 - color);
        else if(col[v] == color){
            res = 1;
            return; 
        }
    }
}

int main(){
    int n, m;
    while(~scanf("%d%d", &n, &m) && n){
        init();
        rep(i, 1, n) dcc[i].clear();
        while(!sta.empty()) sta.pop();
        rep(i, 1, m){
            int u, v;
            scanf("%d%d", &u, &v);
            g[u][v] = g[v][u] = 1;
        }
        rep(i, 1, n)
            rep(j, i + 1, n)
                if(!g[i][j]) add(i, j), add(j, i);
        rep(i, 1, n)
            if(!dfn[i]) tarjan(i, i);
        rep(i, 1, tot){
            now = i;
            rep(j, 0, SZ(dcc[i]) - 1) c[dcc[i][j]] = now, col[dcc[i][j]] = 0;;
            res = 0;
            dfs(dcc[i][0], 1);
            if(res)
                rep(j, 0, SZ(dcc[i]) - 1) able[dcc[i][j]] = 1;
        }
        int ans = 0;
        rep(i, 1, n)
            if(!able[i]) ans++;
        printf("%d\n", ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/SHANAchan/p/10343958.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值