Knights of the Round Table

本文介绍了如何使用点双联通分量和二分图染色来解决骑士会议安排问题。首先,根据题目给出的憎恨关系建图,并求补图确定可坐在一起的骑士。通过Tarjan算法寻找双联通分量,进而利用DFS交叉染色法找出奇圈。奇圈上的骑士无法参加同一场会议,最终通过计数确定不能参加会议的骑士数量。

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

点双联通分量+二分图染色

1、首先根据题目给出的憎恨关系建图,然后求补图,这个图表示哪个骑士可以和哪个骑士坐在一起。

2、在图中求出圈,也就是双联通分量,这可以用到Tarjan算法的思想,通过Tarjan算法执行过程中退栈求出一个又一个双联通分量。

3、对于双联通分量我们需要在其中找到一个奇圈,这需要一个利用了DFS的交叉染色法,在代码中我建补图的时候没有建立自己和自己的边,当用这个方法找出来的奇圈这少有三个点在圈上。

4、对于在奇圈上的标记出来,最后计数一共有多少,就知道不能开会的有多少了

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stack>

using namespace std;

#define MAXN 1111
#define MAXE 2001000

struct Edge
{
    int v, next;
} e[MAXE];

int head[MAXE], cnt;
int dfn[MAXN], low[MAXN],belong[MAXN];
bool insta[MAXN],gra[MAXN][MAXN];;//在不在栈中
int dep, blo;
bool can[MAXN], ok[MAXN];//ok标记点书否属于当前环
int tem[MAXN], color[MAXN];
stack<int> sta;

void init()
{
    memset(head, -1, sizeof(head));
    cnt = dep = blo = 0;
    memset(dfn, 0, sizeof(dfn));
    while(!sta.empty()) sta.pop();
    memset(insta, 0, sizeof(insta));
    memset(gra, 0, sizeof(gra));
    memset(can, 0, sizeof(can));
}

void Addedge(int uu, int vv)
{
    e[cnt].v = vv, e[cnt].next = head[uu];
    head[uu] = cnt++;
}
//dfs染色
bool dfs( int u, int col)
{
    color[u] = col;
    for(int i = head[u];  i != -1; i = e[i].next)
    {
        int v = e[i].v;
        if(!ok[v]) continue;
        if(color[v] != -1)
        {
            if(color[v] == col) return false;
            continue;
        }
        if(!dfs(v, !col)) return false;
    }
    return true;
}

void Tarjan(int u, int pre)
{

    dfn[u] = low[u] = ++dep;
    sta.push(u);
    insta[u] = 1;
    //cout<<"u"<<"   "<<u<<"  ";
    for(int i = head[u]; i != -1; i = e[i].next)
    {


        int v = e[i].v;
        if(v == pre) continue;
        //cout<<"v   "<<v<<" ";
        if(!dfn[v])
        {
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u])
            {
                int vn ;
                int cnt = 0;
                ++blo;
                memset(ok, 0, sizeof(ok));
                do
                {
                    vn = sta.top();
                    sta.pop();
                    insta[vn] = 0;
                    ok[vn] = 1;
                    tem[cnt++] = vn;
                    belong[vn] = blo;
                }while(vn != v);
                ok[u] = 1;
                memset(color, -1, sizeof(color));
                if(!dfs(u, 0))
                {
                    can[u] = 1;
                    for( int k = 0; k < cnt; k++)
                        can[tem[k]] = 1;
                }
            }

        }
        else if(insta[v])
            low[u] = min(low[u], dfn[v]);
    }
    // cout<<endl;
}

void solve( int n)
{
    for( int i = 1; i <= n; i++)
        if(!dfn[i]) Tarjan(i, -1);
    int ans = n;
    for( int i = 1; i <= n; i++)
        if(can[i])  ans --;
    printf("%d\n",ans);
}

int main()
{
    int n, m, u, v;
    while(scanf("%d %d",&n, &m) != EOF &&(n + m) )
    {
        init();
        for( int i = 0; i < m; i++)
        {
            scanf("%d %d",&u, &v);
            gra[u][v] = gra[v][u] = 1;
        }
        for( int i = 1; i <= n; i++)
            for( int j = 1; j <= n; j++)
                if(i != j && gra[i][j] == 0)
                    Addedge(i , j);
        solve(n);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值