LA3523 Knights of the Round Table(双连通分量)

解决骑士参会问题,通过构建图模型并利用双连通分量及奇环判断,确定一定无法参加人数为奇数会议的骑士数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
             n n 个骑士和m组关系,每组关系表示两个骑士相互憎恨,相互憎恨的骑士不能在参加一场会议相邻着坐,而且每次会议参加的人数必须为奇数( 3 ⩾ 3 ),问多少骑士一定不能参加会议。
思路:
             参照刘汝佳入门经典,可以相邻坐的骑士连边,求出双连通分量,然后保证双连通分量里面有奇环即可, 判断是否有奇环可以染色操作。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e3 + 10;
using namespace std;

typedef pair<int, int> pa;
int pre[maxn], iscut[maxn], bccno[maxn];
int dfs_clock, bcc_cnt;
vector<int> G[maxn], bcc[maxn];
stack<pa> stk;
int n, m, T, kase = 1;

int dfs(int u, int fa) {
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        pa e = pa(u, v);
        if(!pre[v]) {
            stk.push(e); child++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if(lowv >= pre[u]) { ///割点
                iscut[u] = 1;
                bcc_cnt++; bcc[bcc_cnt].clear();
                while(1) {
                    pa x = stk.top(); stk.pop();
                    if(bccno[x.first] != bcc_cnt) {
                        bcc[bcc_cnt].push_back(x.first);
                        bccno[x.first] = bcc_cnt;
                    }
                    if(bccno[x.second] != bcc_cnt) {
                        bcc[bcc_cnt].push_back(x.second);
                        bccno[x.second] = bcc_cnt;
                    }
                    if(x.first == u && x.second == v) break;
                }
            }
        } else if(pre[v] < pre[u] && v != fa) {
            stk.push(e);
            lowu = min(lowu, pre[v]);
        }
    }
    if(fa < 0 && child == 1) iscut[u] = 0; ///根节点
    return lowu;
}

int not_can[maxn][maxn];
int can_join[maxn], col[maxn], b[maxn];

bool __find(int x, int c, int blk) {
    col[x] = c;
    for(int i = 0; i < G[x].size(); i++) {
        int v = G[x][i];
        if(b[v] != blk) continue;
        if(col[v] == -1 && __find(v, c ^ 1, blk)) return true;;
        if(~col[v] && col[v] == col[x]) return true;
    }
    return false;
}


int main() {
    while(scanf("%d %d", &n, &m) && (n + m)) {
        memset(not_can, 0, sizeof not_can);
        while(!stk.empty()) stk.pop();
        dfs_clock = bcc_cnt = 0;
        for(int i = 1; i <= n; i++) {
            G[i].clear(); bcc[i].clear();
            can_join[i] = 0; col[i] = -1;
            pre[i] = iscut[i] = bccno[i] = 0;
        }
        while(m--) {
            int u, v; scanf("%d %d", &u, &v);
            not_can[u][v] = not_can[v][u] = 1;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++) {
                if(not_can[i][j]) continue;
                G[i].push_back(j);
                G[j].push_back(i);
            }
        }
        for(int i = 1; i <= n; i++) if(!pre[i]) dfs(i, -1);
        for(int i = 1; i <= bcc_cnt; i++) {
            for(int j = 0; j < bcc[i].size(); j++) {
                b[bcc[i][j]] = i;
                col[bcc[i][j]] = -1;
            }
            bool can = __find(bcc[i][0], 1, i);
            if(!can || bcc[i].size() == 1) continue;
            for(int j = 0; j < bcc[i].size(); j++) can_join[bcc[i][j]] = 1;
        }
        int ans = 0;
        for(int i = 1; i <= n; i++) ans += can_join[i];
        printf("%d\n", n - ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值