Knights of the Round Table--POJ 2942

1、题目类型:图论、点双强连通分量、Tarjan算法。

2、解题思路:题意,N个骑士中某些骑士之间会有仇恨。骑士们开会时围坐在一个圆桌旁。一次会议能够举行,当且仅当没有相邻的两个骑士相互仇恨,且开会人数为大于2的奇数。若某个骑士任何会议都不能参加,那么就必须将它踢出。给出骑士之间的仇恨关系,问需要踢出多少个骑士。步骤,(1)建立输入图的补图;(2)类似Trajan算法求解并记录点双强连通分量;(3)寻找最大强连通分量奇圈。

3、注意事项:两条重要定理:若某块不可染色为二分图,则该块存在奇圈;若某块存在奇圈,那么该块中的所有点都存在与奇圈中;表示图的节点间关系时,用vector表示,用邻接表表示TLE。

4、实现方法:

 
  
#define MAXN 1010
#include
< iostream >
#include
< vector >
using namespace std;

int color[MAXN],use[MAXN],mat[MAXN][MAXN];
vector
< int > map[MAXN];
int n,m,cnt,ans;
int b[MAXN];

bool IsOk( int v, int val)
{
int i,j;
color[v]
= val;
for (j = 0 ;j < map[v].size();j ++ )
{
i
= map[v][j];
if (i == v)
continue ;
if (b[i])
{
if (color[v] == color[i])
return true ;
if (color[i] ==- 1 )
IsOk(i,val
^ 1 );
}
}
return false ;
}

void Dummy( int t, int * a)
{
int i,j;
memset(b,
0 , sizeof (b));
for (j = 0 ;j < t;j ++ )
b[a[j]]
= 1 ;
for (i = 0 ;i < t;i ++ )
{
memset(color,
- 1 , sizeof (color));
if (IsOk(a[i], 1 ))
break ;
}
if (i < t)
{
for (j = 0 ;j < t;j ++ )
{
if ( ! use[a[j]])
{
ans
++ ;
use[a[j]]
= 1 ;
}
}
}
}

void Search( int n,vector < int > mat[MAXN], int * dfn, int * low, int now, int & cnt, int * st, int & sp)
{
int i,j,m,a[MAXN];
dfn[st[sp
++ ] = now] = low[now] =++ cnt;
for (j = 0 ;j < mat[now].size();j ++ )
{
i
= mat[now][j];
if ( ! dfn[i])
{
Search(n,mat,dfn,low,i,cnt,st,sp);
if (low[i] < low[now])
low[now]
= low[i];
if (low[i] >= dfn[now])
{
for (st[sp] =- 1 ,a[ 0 ] = now,m = 1 ;st[sp] != i;a[m ++ ] = st[ -- sp]);
Dummy(m,a);
}
}
else if (dfn[i] < low[now])
low[now]
= dfn[i];
}
}
// 求点双连通分量即块
void Block( int n,vector < int > mat[MAXN])
{
int i,cnt,dfn[MAXN],low[MAXN],st[MAXN],sp = 0 ;
for (i = 0 ;i < n;dfn[i ++ ] = 0 );
for (cnt = i = 0 ;i < n;i ++ )
if ( ! dfn[i])
Search(n,mat,dfn,low,i,cnt,st,sp);
}

void Init()
{
int i,j,a,b;
memset(mat,
0 , sizeof (mat));
for (i = 0 ;i < m;i ++ )
{
scanf(
" %d%d " , & a, & b);
a
-- ;
b
-- ;
mat[a][b]
= mat[b][a] = 1 ;
}
// 转换为补图
for (i = 0 ;i < n;i ++ )
map[i].clear();
for (i = 0 ;i < n;i ++ )
{
for (j = 0 ;j < n;j ++ )
{
if ( ! mat[i][j])
{
map[i].push_back(j);
map[j].push_back(i);
}
}
}
memset(use,
0 , sizeof (use));
cnt
= 0 ,ans = 0 ;
}

int main()
{
while (scanf( " %d%d " , & n, & m))
{
if (n == 0 && m == 0 )
break ;
Init();
Block(n,map);
cout
<< n - ans << endl;
}
return 0 ;
}

 

转载于:https://www.cnblogs.com/yongze103/archive/2010/08/10/1796901.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值