「Note」图论方向 - 图论进阶
生活随笔
收集整理的這篇文章主要介紹了
「Note」图论方向 - 图论进阶
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1. 2-SAT
1.1. 介紹
對于一些節點,每個節點存在兩個狀態(非 \(0\) 即 \(1\)),我們給出一些如下類型的限制條件:
- 節點 \(i\) 狀態為 \(1/0\)。
- 若節點 \(i\) 狀態為 \(1/0\),那么節點 \(j\) 狀態為 \(1/0\)。
- 節點 \(i,j\ (i\not=j)\) 至少有一個為 \(1/0\)。
2-SAT 算法用于解決類似的問題,每個節點最多只能有兩種狀態。
我們用有向圖表示節點狀態之間的遞推關系,我們設 \(p_i\) 表示節點 \(i\) 狀態為真,我們將上述限制條件寫為表達式并寫為“若 \(p\) ,則 \(q\)”的形式,便于用有向圖表示它們的關系:
- \(p_i\):若 \(\lnot p_i\),則 \(p_i\)。
- \(\lnot p_i\):若 \(p_i\),則 \(\lnot p_i\)。
- \(p_i\lor p_j\):若 \(\lnot p_i\) 則 \(p_j\);若 \(\lnot p_j\) 則 \(p_i\)。
- \(\lnot p_i\lor\lnot p_j\):若 \(p_i\) 則 \(\lnot p_j\);若 \(p_j\) 則 \(\lnot p_i\)。
- \(\lnot p_i\lor p_j\):若 \(p_i\) 則 \(p_j\);若 \(\lnot p_j\) 則 \(\lnot p_i\)。
若有 \(p\Rightarrow q\),并且 \(p,q\) 相互矛盾,則 \(p\) 一定為假。
更進一步地,若有 \(p\Leftrightarrow q\),并且 \(p,q\) 相互矛盾,則此限制條件下,無解。
考慮無解的情況在有向圖中的表現形式,即兩個矛盾狀態在圖中處于同一個強連通分量中,我們考慮用 Tarjan 算法進行縮點。
對于一種可行方案則在 \(p_i,\lnot p_i\) 兩個狀態點中選擇拓撲序更小的即可,用 Tarjan 后可省去拓撲排序過程,詳見我其他博客。
1.2. 例題
\(\color{royalblue}{P4782}\)
板子題。
$\text{Code}$:
#include<bits/stdc++.h>
#define LL long long
#define UN unsigned
using namespace std;
//--------------------//
//IO
inline int rd()
{
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
//--------------------//
const int N=2e6+5;
int n,m;
//--------------------//
struct Edge
{
int to,nex;
}edge[N];
int tot,head[N];
void add(int from,int to)
{
edge[++tot]={to,head[from]};
head[from]=tot;
return;
}
int dcnt,dfn[N],low[N];
int sccnt,scc[N];
int stop,sta[N];
bool stv[N];
void Tarjan(int now)
{
low[now]=dfn[now]=++dcnt;
sta[++stop]=now,stv[now]=true;
for(int to,i=head[now];i;i=edge[i].nex)
{
to=edge[i].to;
if(!dfn[to])
{
Tarjan(to);
low[now]=min(low[now],low[to]);
}
else if(stv[to])
low[now]=min(low[now],dfn[to]);
}
if(low[now]==dfn[now])
{
scc[now]=++sccnt;
stv[now]=false;
while(sta[stop]!=now)
{
scc[sta[stop]]=sccnt;
stv[sta[stop]]=false;
stop--;
}
stop--;
}
return;
}
//--------------------//
int main()
{
scanf("%d%d",&n,&m);
for(int a,x,b,y,i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a,&x,&b,&y);
add(a+(!x)*n,b+y*n);
add(b+(!y)*n,a+x*n);
}
for(int i=1;i<=2*n;i++)
if(!dfn[i])
Tarjan(i);
for(int i=1;i<=n;i++)
if(scc[i]==scc[i+n])
{
printf("IMPOSSIBLE\n");
return 0;
}
printf("POSSIBLE\n");
for(int i=1;i<=n;i++)
if(scc[i]<scc[i+n])
printf("0 ");
else
printf("1 ");
return 0;
}
總結
以上是生活随笔為你收集整理的「Note」图论方向 - 图论进阶的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 车险到期了晚点买行吗 这些知识需要了解
- 下一篇: 农商银行和农村信用社一样吗 两者性质不一