HDU 4751 Divide Groups (2-SAT)

题意

给定一个有向图,问是否能够分成两个有向完全图。

思路

裸的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表示组数. //(通常一组表示一个布尔变量的0\1值, 但也有题目表示的是一组互斥约束的布尔值) 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]

你可能感兴趣的:(group)