2-sat

模板

void addedge(int u,int v){
	g[u].pb(v);
	rg[v].pb(u);
}
void dfs(int u){
	vis[u]=1;
	for(int i=0;i<g[u].size();i++)
		if(!vis[g[u][i]])
			dfs(g[u][i]);
	cb.pb(u);
}
void rdfs(int u,int k){
	vis[u]=1;
	cmp[u]=k;
	for(int i=0;i<rg[u].size();i++)
		if(!vis[rg[u][i]])
			rdfs(rg[u][i],k);
}
int n,m;
int scc(){///返回强连通分量的数量
	for(int i=0;i<2*n;i++)
		if(!vis[i])
			dfs(i);
	int k=0;
	memset(vis,0,sizeof(vis));
	for(int i=cb.size()-1;i>=0;i--){
		if(!vis[cb[i]])
			rdfs(cb[i],k++);
	}
	return k;
}

  

提:http://acm.hdu.edu.cn/showproblem.php?pid=3062

题目思路:

                 对于这类问题一般的解法为将每个点分为 x 和 x' 分别表示每组当中的两种状态,这里表示丈夫和妻子,然后更具定义我们建图,如果 A和B有矛盾则选A必选B',选B必选A' ,所以我们对该图缩点,如果A和A'属于同一个环当中则说明选A必选A',所以不存在,否则就存在

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define pb push_back
const int M=5000;
vector<int>g[M];
int low[M],dfn[M],sta[M],vis[M],cmp[M];
int tot,cnt,top;
void tarjan(int u){
    low[u]=dfn[u]=++cnt;
    sta[++top]=u;
    vis[u]=1;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        tot++;
        cmp[u]=tot;
        vis[u]=0;
        while(sta[top]!=u){
            cmp[sta[top]]=tot;
            vis[sta[top--]]=0;
        }
        top--;
    }
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<=2*n;i++)
            g[i].clear(),low[i]=dfn[i]=cmp[i]=vis[i]=sta[i]=0;
        top=cnt=tot=0;
        while(m--){
            int a1,a2,c1,c2;
            scanf("%d%d%d%d",&a1,&a2,&c1,&c2);
            int a = a1*2+c1;
            int b = a2*2+c2;
            g[a].pb(b^1);
       //     cout<<u<<"----"<<2*a2+(1-c2)<<endl;
      //      g[2*a2+(1-c2)].pb(u);
            g[b].pb(a^1);
       //     cout<<v<<"----"<<2*a1+(1-c1)<<endl;
      //      g[2*a1+(1-c1)].pb(v);
        }
        for(int i=0;i<2*n;i++)
            if(!dfn[i])
                tarjan(i);
        int flag=1;
        for(int i=0;i<n;i++){
            if(cmp[2*i]==cmp[2*i+1]){
                flag=0;
                break;
            }

        }
        if(flag)
            puts("YES");
        else
            puts("NO");

    }

    return 0;
}
View Code

 例题:输出满足条件的方案是什么

题:http://acm.hdu.edu.cn/showproblem.php?pid=1814

把所有能到达的点标记一下,顺便判断一下和之前有没有矛盾,有矛盾的话所有被标记的点又要重新标记回去。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define pb push_back
const int M=2e4+4;
vector<int>g[M];
int vis[M],s[M],tot,n;
bool dfs(int u){
    if(vis[u])
        return true;
    if(vis[u^1])
        return false;
    vis[u]=1;
    s[tot++]=u;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!dfs(v))
            return false;
    }
    return true;
}
bool solve(){
    for(int i=0;i<2*n;i+=2){
        if(vis[i]||vis[i^1])
            continue;
        tot=0;///相当于缩点过程 
        if(!dfs(i)){
            while(tot)
                vis[s[--tot]]=0;
            if(!dfs(i^1))
                return false;
        }
    }
    return true;
}
int main(){
    int m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<=2*n;i++)
            g[i].clear(),vis[i]=0;
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            u--,v--;
            g[u].pb(v^1);
            g[v].pb(u^1);
        }
        if(solve()){
            for(int i=0;i<2*n;i++)
                if(vis[i])
                    printf("%d\n",i+1);
        }
        else
            puts("NIE");
    }
    
    return 0;
}
View Code

 题:https://codeforces.com/problemset/problem/468/B

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int M=2e5+5;
struct node{
    int val,id;
    bool operator<(const node &b)const{
        return val<b.val;
    }
}a[M];
vector<int>g[M],rg[M],newg;
int vis[M],cmp[M];
void addedge(int u,int v){
    g[u].pb(v);
    rg[v].pb(u);
}
void dfs(int u){
    vis[u]=1;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!vis[v])
            dfs(v);
    }
    newg.pb(u);
}
void rdfs(int u,int k){
    vis[u]=1;
    cmp[u]=k;
    for(int i=0;i<rg[u].size();i++){
        int v=rg[u][i];
        if(!vis[v])
            rdfs(v,k);
    }
}
int n,A,B;
int scc(){
    for(int i=0;i<2*n;i++)
        if(!vis[i])
            dfs(i);
    memset(vis,0,sizeof(vis));
    int k=0;
    for(int i=newg.size()-1;i>=0;i--){
        int v=newg[i];
        if(!vis[v])
            rdfs(v,k++);
    }
}
int main(){
    
    scanf("%d%d%d",&n,&A,&B);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i].val),a[i].id=i;
    sort(a,a+n);
    for(int i=0;i<n;i++){
        int pos=lower_bound(a,a+n,node{A-a[i].val,0})-a;
        if(pos!=n&&a[pos].val==A-a[i].val){
            addedge(a[i].id,a[pos].id);
            addedge(a[pos].id+n,a[i].id+n);
        }
        else{
            addedge(a[i].id,a[i].id+n);
        }
        
        pos=lower_bound(a,a+n,node{B-a[i].val,0})-a;
        if(pos!=n&&a[pos].val==B-a[i].val){
            addedge(a[i].id,a[pos].id);
            addedge(a[pos].id+n,a[i].id+n);
        }
        else{
            addedge(a[i].id+n,a[i].id);///这一步与上面处理相反是为了在输出归属方案时区别开来 
        }
    //    cout<<pos<<endl;
    }
    int k=scc();
    for(int i=0;i<n;i++){
        if(cmp[i]==cmp[i+n])
            return puts("NO"),0;
    }
    puts("YES");
    for(int i=0;i<n;i++){
        if(cmp[i]>cmp[i+n])
            printf("0 ");
        else
            printf("1 ");
    }
    return 0;
}
View Code

 题:http://poj.org/problem?id=3207

题意:平面上,一个圆,圆的边上按顺时针放着n个点。现在要连m条边,
比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接。
给你的信息中,每个点最多只会连接的一条边。问能不能连接这m条边,
使这些边都不相交。
#include<vector>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
#define pb push_back
const int M=2003;
vector<int>g[M],rg[M],cb;
int vis[M],cmp[M];
void addedge(int u,int v){
    g[u].pb(v);
    rg[v].pb(u);
}
void dfs(int u){
    vis[u]=1;
    for(int i=0;i<g[u].size();i++)
        if(!vis[g[u][i]])
            dfs(g[u][i]);
    cb.pb(u);
}
void rdfs(int u,int k){
    vis[u]=1;
    cmp[u]=k;
    for(int i=0;i<rg[u].size();i++)
        if(!vis[rg[u][i]])
            rdfs(rg[u][i],k);
}
int n,m;
int scc(){
    for(int i=0;i<2*n;i++)
        if(!vis[i])
            dfs(i);
    int k=0;
    memset(vis,0,sizeof(vis));
    for(int i=cb.size()-1;i>=0;i--){
        if(!vis[cb[i]])
            rdfs(cb[i],k++);
    }
    return k;
}
int a[M];
vector<int>fuck;
int main(){
    memset(a,-1,sizeof(a));
    scanf("%d%d",&n,&m);
    for(int u,v,i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        a[u]=v;
        a[v]=u;
        fuck.pb(u);
        fuck.pb(v); 
    }
    sort(fuck.begin(),fuck.end());
    for(int i=0;i<fuck.size();i++){
        for(int j=i+1;j<fuck.size();j++){
            int l=min(fuck[i],a[fuck[i]]),r=max(fuck[i],a[fuck[i]]);
            int L=min(fuck[j],a[fuck[j]]),R=max(fuck[j],a[fuck[j]]);
            if((l<L&&L<r&&R>r)||l<R&&R<r&&l>L){///代表这俩条线不能同时在圆内或圆外,即俩者矛盾 
                addedge(fuck[i],fuck[j]+n);
                addedge(fuck[j],fuck[i]+n);
                addedge(fuck[i]+n,fuck[j]);
                addedge(fuck[j]+n,fuck[i]);
            }
        }
    }
    int k=scc();
    for(int i=0;i<fuck.size();i++){
        int v=fuck[i];
        if(cmp[v]==cmp[v+n])
            return puts("the evil panda is lying again"),0;
    }
        
    puts("panda is telling the truth...");
    return 0;
}
View Code

 



 

posted @ 2019-10-27 15:48  starve_to_death  阅读(122)  评论(0编辑  收藏  举报