POJ 2942 Knights of the Round Table 【点双联通 + 二分图染色法判奇环】

版权声明:本文为博主原创文章,喜欢就点个赞吧 https://blog.csdn.net/Anxdada/article/details/79361906

传送门
亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:

1、 相互憎恨的两个骑士不能坐在直接相邻的2个位置;

2、 出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。

如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。

现在给定准备去开会的骑士数n,再给出m对憎恨对(表示某2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议?

注意:1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。

2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。

3、一个骑士无法开会,就是说至少有3个骑士才可能开会。

思路: 很明显我们可以根据m对憎恨关系建图, 也就是两点之间如果不憎恨就有一条边, 然后我们的目的就是找到图中存在于奇环中的那些点, ans = n - 那些点. 那么对于我们找到了一个环如何判断它的个数是奇数个点了(或者这个偶数大环可以被分割若干个奇环). 那么就要用到这个定理了, 是如果是偶数个数(并且内部不能分割成小奇环)的环那么它就是一个二分图, 所以我们用染色法判定下该环是不是二分图即可.

因为二分图的定义: 不含有含有奇数条边的环的图

AC Code

扫描二维码关注公众号,回复: 3330321 查看本文章
const int maxn = 1e3 + 5;
int cas=1;
int dfn[maxn], low[maxn], cut[maxn];
int n, m;
int cnt, head[maxn];
int dfs_id, cut_num;
int a[maxn], can[maxn], g[maxn][maxn];
bool vis[maxn];
stack<int >st;
struct node
{
    int from, to, next;
}e[maxn*maxn];

void add(int u, int v)
{
    e[cnt] = node{u, v, head[u]};
    head[u] = cnt++;
}
void init()
{
    Fill(dfn, 0); Fill(low, 0); Fill(cut, 0);
    cnt = 0; Fill(head, -1); Fill(can, 0);
    Fill(a, 0); Fill(g, 0);
    dfs_id = cut_num = 0;
}
int color[maxn], flag;
void dfs(int u, int fa, int col)
{
    if(!flag) return ;   //flag=false, 后面就都没有必要再搜下去了.
    if(!color[u]) color[u] = col;  //如果该点没有被染色,就染上.
    else if(color[u] != col){   //如果遇到将要染色的点不等于将要被染的色,则结束dfs,不是二分图.
        flag = false;
        return ;
    }
    else return ;
    for(int i = head[u] ; ~i ; i = e[i].next) {
        int to = e[i].to;
        if (to == fa || !vis[to]) continue; // 只走在环中的点.
        dfs(to, u, 3 - col);
    }
}
void tarjan(int u,int fa)
{
    int son = 0;
    dfn[u] = low[u] = ++dfs_id;
    for(int i = head[u] ; ~i ; i = e[i].next) {
        int v = e[i].to;
        if(v == fa) continue;
        if(!dfn[v]){
            son++; st.push(i);
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]){
                cut[u] = 1; int k = 0;
                Fill(vis, false);
                while (1) {
                    int id = st.top(); st.pop();
                    if (!vis[e[id].from]) a[++k] = e[id].from;
                    if (!vis[e[id].to]) a[++k] = e[id].to;
                    vis[e[id].from] = vis[e[id].to] = true;
                    if (e[id].from == u && e[id].to == v)
                        break;
                }
                Fill(color, 0); flag = 1;
                if (k >= 3) dfs(u, -1, 1); // 判定该环.
                if (!flag) { // 不是二分图, 也就是我们满足条件的点.
                    for (int i = 1 ; i <= k ; i ++) {
                        can[a[i]] = 1;
                    }
                }
            }
        }
        else if(dfn[v] < dfn[u]) {
            st.push(i);
            low[u] = min(low[u],dfn[v]);
        }
    }
    if (fa == -1 && son > 1) cut[u] = 1;
}

void solve()
{
    while(~scanf("%d%d", &n, &m)){
        if (n + m == 0) break;
        init();
        for (int i = 1 ; i <= m ; i ++) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u][v] = g[v][u] = 1;
        }
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = i + 1 ; j <= n ; j ++) {
                if (!g[i][j]) {
                    add(i, j); add(j, i);
                }
            }
        }
        for(int i = 1 ; i <= n ; i ++) {
            if(!dfn[i]) tarjan(i, -1);
        }
        int ans = 0;
        for (int i = 1 ; i <= n ; i ++) {
            if (!can[i]) ans++;
        }
        printf("%d\n", ans);
    }
}

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/79361906