题目大意:将n个人分成两个组,能不能使得每组中达到一个人人都相互认识的最佳状态?
解题思路:开始想的是根据给出的关系图,将分为两个组,这两个组是不是一个有向完全图。后边看了下大神的解法,此题有三种解法,分别可以用DFS、BFS和2-SAT来解。前面两种都很容易想到,但第三种不好想到,不过这样的写法很巧妙的,对于已经是双向连接的,可以不予处理,而对于单向的或者是没有连接的,要利用2-SAT的特征进行构造,即两个结点不能再同一个集合的问题。参照白书上的模板写了下,速度都比前两种的快,细节可以参见白书上的,解法详见code。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=4751
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 100+10;
int n,map[MAXN][MAXN];
struct twosat{ //白书模板
int n;
vector<int> G[MAXN*2];
bool mark[MAXN*2];
int S[MAXN*2],c;
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<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
void init(int n){ //初始化
this->n=n;
for(int i=0;i<n*2;i++) G[i].clear(); //清空图
memset(mark,0,sizeof(mark));
}
void add_clause(int x,int y){
x=x*2;y=y*2; //拆分结点
G[x].push_back(y+1);G[y+1].push_back(x); //标记结点
G[y].push_back(x+1);G[x+1].push_back(y);
}
bool solve(){
for(int i=0;i<n*2;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;
}
}tsat;
int main(){
while(scanf("%d",&n)!=EOF){
tsat.init(n+1); //初始化
memset(map,0,sizeof(map));
for(int i=1,u;i<=n;i++) //获取边的关系
while(scanf("%d",&u) && u)
map[i][u]=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if(map[i][j] && map[j][i]) continue; //如果是双向的,则不处理
else{
map[i][j]=map[j][i]=0; //如果是单向,或者不连通的将其置为0
tsat.add_clause(i,j);
}
}
bool ans=tsat.solve();
if(ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}