题意
给定一个有向图,问是否能够分成两个有向完全图。
思路
裸的2-sat……我们设一个完全图为0,另一个完全图为1,对于一个点对(u, v),如果u、v不是双向连通则它们两个不能在一组,即u和v至少又一个为0,至少又一个为1。则我们向2-sat中加条u->v', u'->v, v->u', u'->v的边,然后验证可行性即可。
(关于2-SAT的建图可以见
这篇题解)
代码
[cpp]
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, end) for (int i = begin; i <= end; i ++)
using namespace std;
const int MAXN = 2005;
const int MAXE = MAXN * MAXN;
bool map[105][105];
struct node{
int u, v;
int next;
}arc[MAXE];
int cnt, head[MAXN];
void init(){
cnt = 0;
MEM(head, -1);
return ;
}
void insert(int u, int v){
arc[cnt].u = u;
arc[cnt].v = v;
arc[cnt].next = head[u];
head[u] = cnt ++;
}
/* ---- Tarjan ---- */
int scc_num, scc[MAXN];
int dfn[MAXN], low[MAXN], id;
stack st;
bool vis[MAXN], instack[MAXN];
void dfs(int u){
vis[u] = instack[u] = 1;
st.push(u);
dfn[u] = low[u] = ++ id;
for (int i = head[u]; i != -1; i = arc[i].next){
int v = arc[i].v;
if (!vis[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]){
++ scc_num;
while(st.top() != u){
scc[st.top()] = scc_num;
instack[st.top()] = 0;
st.pop();
}
scc[st.top()] = scc_num;
instack[st.top()] = 0;
st.pop();
}
return ;
}
void tarjan(int n){
MEM(vis, 0);
MEM(instack, 0);
MEM(dfn, 0);
MEM(low, 0);
MEM(scc, 0);
id = scc_num = 0;
while(!st.empty())
st.pop();
for (int i = 1; i <= n; i ++){ //枚举节点
if (!vis[i])
dfs(i);
}
return ;
}
/* ---- Tarjan ---- */
int opp[MAXN]; //与i同组的标号i', 默认设为i+N;
void set_opp(int N){ //设定同组标号.
for (int i = 1; i <= N; i ++)
opp[i] = i + N;
return ;
}
//根据y约束向构图中加边,N表示组数.
//(通常一组表示一个布尔变量的01值, 但也有题目表示的是一组互斥约束的布尔值)
void add_clause(int N){
init();
set_opp(N);
for (int i = 1; i <= N; i ++){
for (int j = 1; j <= N; j ++){
if (i == j || (map[i][j] && map[j][i])) continue;
insert(i, opp[j]);
insert(opp[i], j);
insert(j, opp[i]);
insert(opp[j], i);
}
}
return ;
}
bool check(int N){ //2-SAT判定.N表示组数.opp[i]表示与i同互斥组的另一个点
tarjan(2*N);
for (int i = 1; i <= 2*N; i ++){
if (scc[i] == scc[opp[i]]){
return false;
}
}
return true;
}
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int n;
while(scanf("%d", &n) != EOF){
MEM(map, 0);
for (int i = 1; i <= n; i ++){
int tmp;
while(scanf("%d", &tmp), tmp){
map[i][tmp] = 1;
}
}
add_clause(n);
if (check(n)){
puts("YES");
}
else{
puts("NO");
}
}
return 0;
}
[/cpp]