题目:http://acm.hdu.edu.cn/showproblem.php?pid=1824
第一次写2-SAT,纪念一下。
根据题目的描述,大概分为几种情况:
同一队伍的三个人A,B,C产生的限制是:A回去=>B留下,A回去=>C留下,B回去=>A留下,C回去=>A留下
一对人A和B的限制是:A留下=>B回去,B留下=>A回去
按照上面的方法建立有向图,然后进行强联通分量的缩点,如果存在某个X,使得X回去和X留下存在同一个强连通分量,则说明有矛盾,无法满足。
如果找不到冲突,那就是yes了。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
using namespace std;
#define N 6000
#define pb push_back
vector<int> V[N];
int t, n, m;
int pre[N], low[N], sccno[N], dfs_clock, scc_cnt;
stack<int> S;
void dfs(int x){
pre[x] = low[x] = ++dfs_clock;
S.push(x);
for(int i=0; i<V[x].size(); i++){
int j=V[x][i];
if(!pre[j]){
dfs(j);
low[x] = min(low[x],low[j]);
}
else if(!sccno[j]){
low[x] = min(low[x],pre[j]);
}
}
if(low[x]==pre[x]){
scc_cnt++;
for(;;){
int u=S.top();S.pop();
sccno[u]=scc_cnt;
if(x==u) break;
}
}
}
void find_scc(){
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
for(int i=0; i<2*n; i++){
if(!pre[i]) dfs(i);
}
}
void add(int x,int y){
V[x].pb(y);
}
bool solve(){
find_scc();
for(int i=0; i<n; i++){
if(sccno[i]==sccno[i+n]) return 0;
}
return 1;
}
int main(){
while(~scanf("%d %d", &t, &m)){
n = t*3;
for(int i=0; i<2*n; i++) V[i].clear();
int x, y, z;
for(int i=0; i<t; i++){
scanf("%d %d %d", &x, &y, &z);
add(y+n, x);
add(z+n, x);
add(x+n, y);
add(x+n, z);
}
while(m--){
scanf("%d %d", &x, &y);
add(x, y+n);
add(y, x+n);
}
puts(solve()?"yes":"no");
}
return 0;
}