poj-2942-Knights of the Round Tabler

39 篇文章 0 订阅

题目大意:
有N个骑士,他们要开圆桌会议,也就是要坐成一个圈,相互憎恨的两个骑士是不能坐在相邻位置的,那样他们就会打起来。给出所有的憎恨关系。如果有人不可能开会,例如他可能憎恨所有人,就不能再去开会了。求这样人的个数。

注意:1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。
2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。
3、一个骑士无法开会,就是说至少有3个骑士才可能开会。

思路:
1. 求补图(补图:图G的补图~G就是把图G原有的边全部删去,原本不存在的边全部连上。 )
2. 求双连通分量(双连通分量: 简单来说,无向图G如果是双连通的,那么至少要删除图G的2个结点才能使得图G不连通。换而言之,就是图G任意2个结点之间都存在两条以上的路径连接(注意:路径不是指直接相连的边),那么双连通分量就是指无向图G的子图G’是双连通了。 )
3. 找奇圈(奇圈是用一条线把奇数个点串连起来,所得到的闭合的圈就是奇圈了。其实奇圈就是有奇数个顶点的环。 )

一个重要的性质
若某块存在奇圈,那么该块中的所有点都存在与奇圈中

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <string>
#include <algorithm>
#define ll long long
using namespace std;
const int inf=0x3ffffff;
const int MAXN = 1003;
const double eps = 1e-6;
int edge[MAXN][MAXN];
int dfn[MAXN], low[MAXN], in_seq[MAXN];
int stack[MAXN], list[MAXN];
int cnt, top, pop, len;
int n, m;
bool flag[MAXN];
int color[MAXN];

bool paint(int u, int c){  //找奇圈 
    color[u] = c;
    for (int i = 1; i <= n; i++) {
        if (edge[u][i] && in_seq[i] == cnt) {
            if (color[i] && color[i] == color[u]) {
                return true;
            }
            if (!color[i] && paint(i, -c)) {
                return true;
            }
        }
    }
    return false;
}

void biconnect(int v) {
    stack[++top] = v;
    dfn[v] = low[v] = pop++;
    int i;
    for (i = 1; i <= n; i++) {
        if (edge[v][i]){
            if (dfn[i] == -1) {
                biconnect(i);
                if (low[i] >= dfn[v]) {
                    cnt++;
                    len = 0;
                    do {
                        in_seq[stack[top]] = cnt;
                        list[len++] = stack[top];
                        top--;
                    } while(stack[top + 1] != i);
                    in_seq[v] = cnt;
                    list[len++] = v;
                    memset(color, 0, sizeof(color));
                    if (len >= 3 && paint(i, 1)){
                        for (int j = 0; j < len; j++) {
                            flag[list[j]] = true;
                        }
                    }
                }
                low[v] = min(low[v], low[i]);
            } else {
                low[v] = min(low[v], dfn[i]);
            }
        }
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("1.txt", "r", stdin);
#endif
    int i, j, k, u, v; 
    while(cin >> n >> m, n) {
        cnt = top = pop = 0;
        memset(in_seq, 0, sizeof(in_seq));
        memset(dfn, -1, sizeof(dfn));
        memset(flag, false, sizeof(flag));
        for (i = 0; i <= n; i++) {
            for (j = 0; j < i; j++) {
                edge[i][j] = edge[j][i] = true;
            }
            edge[i][i] = false;
        }
        for (i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            edge[u][v] = edge[v][u] = false; //建立反向图 
        }
        for (i = 1; i <= n; i++) {
            if (dfn[i] == -1) {
                biconnect(i);
            }
        }
        int ans = 0;
        for (i = 1; i <= n; i++) {
            ans += flag[i];
        }
        cout << n-ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值