题目链接:
题目大意:
给出一些党派,每个党派有两个代表,某些不同的代表之间存在矛盾,每个党派派出一个代表去开会,会议能够正常召开吗?如果能够正常召开,那么输出最小字典序的解
题目分析:
首先对于给出一个关系,我们对于任意党派A中两个代表a和a`,那么如果a和b有矛盾,那么我们选a就一定要选b`,选b就一定要选a`,所以建两条有向边,其中有向边<u,v>,表示选u一定要选v,那么我们可以开一个数组,mark[i]表示某一个点的状态是被选还是未被选,对于一个强连通分量,状态必须都一致,否则就是矛盾的,那么整个强连通的分量不能得到一致的解,那么证明无解,因为是深搜,所以最后得到的结果一定是字典序。
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#define MAX 8007
using namespace std;
int n,m;
struct TwoSat
{
int n;
vector<int> e[MAX<<1];
int s[MAX<<1],c;
bool mark[MAX<<1];
//mark[i<<1]数组等于1,表示点i被选择
//mark[i<<1|1]数组等于1,表示点i没有被选择
bool dfs ( int x )
//用来判断当前的强连通分量当中会不会出现矛盾
{
//如果需要被选的不能被选那么矛盾
if ( mark[x^1] ) return false;
//如果需要被选的已经被选,那么当前联通分量一定
//不会出现矛盾
if ( mark[x] ) return true;
//如果当前点需要被选,那么选上它,并且标记
mark[x] = true;
//当前的强连通分量加上这个点
s[c++] = x;
//找到与当前点相连点,判断他们的状态
for ( int i = 0 ; i <e[x].size() ; i++ )
if ( !dfs( e[x][i] ))
return false;
return true;
}
void init ( int n )
{
this->n = n;
for ( int i = 0 ; i < 2*n ; i++ )
e[i].clear();
memset ( mark , 0 , sizeof ( mark ));
}
void add ( int x , int y )
{
e[x].push_back ( y^1 );
e[y].push_back ( x^1 );
}
bool solve ( )
{
for ( int i = 0 ; i < 2*n ; i += 2 )
if ( !mark[i] && !mark[i+1] )
{
c = 0;
if ( !dfs(i) )
{
//如果矛盾,那么这个强连通分量里的点都不能
//选取
while ( c > 0 ) mark[s[--c]]= false;
if ( !dfs(i+1) ) return false;
}
}
return true;
}
void print ( )
{
if (!solve()) puts ( "NIE" );
else
{
for ( int i = 0 ; i < 2*n ; i++ )
if ( mark[i] )
printf ( "%d\n" , i+1 );
}
}
}TS;
int main ( )
{
while ( ~scanf ( "%d%d" , &n , &m ))
{
TS.init( n );
while ( m-- )
{
int a,b;
scanf ( "%d%d" , &a , &b );
a-- , b--;
TS.add ( a , b );
}
TS.print();
}
}