Tarjan/2-SAT學習筆記


Tarjan/2-SAT

Tags:圖論

作業部落

評論地址


Tarjan

用來求割邊或者割點,求點雙聯通分量或者邊雙聯通分量
點雙聯通分量:兩個點之間有兩條點不相交的路徑
邊雙聯通分量:兩個點之間有兩條邊不相交的路徑
Tarjan求LCA還不會

2-SAT

每種物品有選或者不選兩種狀態,有些限制條件形如
選了\(A\)則必須選\(B\)\(A\)\(B\)不能同時選,必須選\(A\)等等
把邏輯限制關系變成連邊
a->b表示如果\(a\)成立那么\(b\)一定成立
這個要求你理解逆否命題
逆否命題,舉個例子,選\(A\)必須選\(B\),那么我選了\(B'\)就不能選\(A\),選\(B'\)就必須選\(A'\)
由於逆否命題產生的對稱性使得\(2-SAT\)問題得以在\(O(n)\)時間求解
具體來說要求同時連接x->y y'->x'
這樣跑一遍\(Tarjan\)縮點后如果統一物品的兩種狀態在同一個邊雙連通分量中就無解
否則可以輸出方案,具體來說是每個點選擇\(Tarjan\)縮成的超級點中編號最小的那個(也就是反圖拓撲序最小的那個)

題單

Tarjan

2-SAT

Code

邊雙

void Tarjan(int x)
{
    vis[x]=1;sta[++top]=x;
    dfn[x]=low[x]=++tot;
    for(int i=A.head[x],R=A.a[i].to;i;i=A.a[i].next,R=A.a[i].to)
        if(!dfn[R]) Tarjan(R),low[x]=min(low[x],low[R]);
        else if(vis[R]) low[x]=min(low[x],low[R]);
    if(low[x]!=dfn[x]) return;
    for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
        vis[k]=0,bel[k]=x,val[x]+=val[k]*(k!=x);
}

點雙

void Tarjan(int x,int f)
{
    int s=0;dfn[x]=low[x]=++tot;
    for(int i=head[x];i;i=a[i].next)
    {
        int R=a[i].to;if(R==f) continue;
        if(dfn[R]) {low[x]=min(low[x],dfn[R]);continue;}
        s++;Tarjan(R,x);tag[x]|=low[R]>=dfn[x];
        low[x]=min(low[x],low[R]);
    }
    if(!f&&s==1) tag[x]=0;
}

!注意點雙第7行一定要是dfn[R]

2-sat(和平委員會)

#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
const int N=41000;
struct edge {int next,fr,to;};
struct Map
{
	edge a[N]; int head[N],cnt;
	void link(int x,int y) {a[++cnt]=(edge){head[x],x,y};head[x]=cnt;}
}A,B;
int dfn[N],top,sta[N],vis[N],low[N];
int bel[N],tot,n,m,p[N],node;
queue<int> Q;
void Min(int &a,int b) {if(b<a) a=b;}
void Tarjan(int x)
{
	vis[x]=1;sta[++top]=x; dfn[x]=low[x]=++tot;
	for(int i=A.head[x],R=A.a[i].to;i;i=A.a[i].next,R=A.a[i].to)
		if(!dfn[R]) Tarjan(R),Min(low[x],low[R]);
		else if(vis[R]) Min(low[x],low[R]);
	if(low[x]!=dfn[x]) return;++node;
	for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
		vis[k]=0,bel[k]=node;
}
int main()
{
//    freopen("spo.in","r",stdin);
//    freopen("spo.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n*2;i++) p[i]=i&1?i+1:i-1;
	for(int i=1;i<=m;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		A.link(x,p[y]);A.link(y,p[x]);
	}
	for(int i=1;i<=n*2;i++) if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n*2;i+=2) if(bel[i]==bel[p[i]]) {puts("NIE");exit(0);}
	for(int i=1;i<=n*2;i+=2) printf("%d\n",bel[i]<bel[p[i]]?i:p[i]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM