使用強連通分量。對於每個變數 x,建立兩個點:x,¬x 分別表示變數 x取true
和取false
。
圖的節點個數是兩倍的變數個數。在儲存方式上,可以給第 i個變數標號為 i,其對應的反值標號為 i+n
。
對於每個要求(a∨
b),轉換為 ( ¬a→b )∧(¬b→a ) ,即:「若 a假則 b 必真,若 b 假則 a 必真」。
然後按照箭頭的方向建有向邊,進行相關的縮點、拓撲排序操作,達到題目要求。
1.【洛谷p4782】模板:2-sat問題
#include#include#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
typedef
long
long
ll;#define r register
/*【p4782】2-sat問題
有n個布林變數x1~xn,另有m個需要滿足的條件,
每個條件的形式都是「xi為true/false或xj為true/false」。
2-sat問題的目標是給每個變數賦值使得所有條件得到滿足。
*/void reads(int &x)
while(s>='
0'&&s<='9')
x*=f; //
正負號}
const
int n=2500019,m=5000019
;int
n,m,dfn[n],low[n],stack[n],vis[n];
int dfn_=0,top_=0,colnum=0,col[n];//
siz[n];
int head[n],tot=0,rd[n],okk=1
;struct nodee[m];
inline
void add(r int x,r int
y)inline
void tarjan(int
x) if(dfn[x]==low[x]) top_--; //
col陣列記錄每個點所在連通塊的編號}}
intmain() for(r int i=1;i<=n*2;i++) if(!dfn[i]) tarjan(i);
for(r int i=1;i<=n;i++) if(col[i]==col[i+n])
if(okk)
else printf("
impossible\n
"); return0;
}
2.【洛谷p4171】滿漢全席
#include#include#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
typedef
long
long
ll;#define r register
/*【p4171】滿漢全席
n個材料,每個材料可選滿式或漢式,m個評委各有喜好。
必須滿足每個評委喜歡的兩道菜之一,問是否可能。
*///
對於每樣材料i拆成兩個點,結點i表示滿式,結點i+n表示漢式。
/*每個評委的限制條件都可以看做『或』的形式:
1.mi,mj:連邊i+n到j,表示若i為漢式,則j必須為滿式;
連邊j+n到i,表示若j為漢式,則i必須為滿式
2.mi,hj:連邊i+n到j+n,表示若i為漢式,則j必須為漢式;
連邊j到i,表示若j為滿式,則i必須為滿式
3.hi,hj:連邊i到j+n,表示若i為滿式,則j必須為漢式;
連邊j到i+n,表示若j為滿式,則i必須為漢式
4.hi,mj:連邊i到j,表示若i為滿式,則j必須為滿式;
連邊j+n到i+n,表示若j為漢式,則i必須為漢式
*///
建圖後tarjan求強連通分量, 判斷i和i+n是否屬於同乙個強連通分量,即可行性。
void reads(int &x)
while(s>='
0'&&s<='9')
x*=f; //
正負號}
const
int n=519,m=2519
;int
n,m,dfn[n],low[n],stack[n],vis[n];
int dfn_=0,top_=0,colnum=0
,col[n],siz[n];
int head[n],tot=0,rd[n],okk=0
;struct nodee[m];
inline
void add(r int x,r int
y)inline
void tarjan(int
x) if(dfn[x]==low[x]) top_--; //
col陣列記錄每個點所在連通塊的編號}}
void
init()
char s1[519],s2[519
];int
main()
else
if(s1[0]=='h'
) }
for(r int i=1;i<=n*2;i++) if(!dfn[i]) tarjan(i);
for(r int i=1;i<=n;i++) if(col[i]==col[i+n])
if(okk) printf("
good\n
"); else printf("
bad\n");
}}
3.【poj3207】panda's trick
#include#include#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
typedef
long
long
ll;#define r register
/*【poj3207】panda's trick
平面上有乙個圓,圓的邊上按順時針放著n個點。
現在要連m條邊(a,b),a到b可以從圓的內部或外部連線。
問能不能連線這m條邊,使這些邊都不相交。
*//*
【分析】設邊i連線點a,b,邊j連線點c,d
線段i與j在圓內是否相交就是線段ab與線段cd是否相交
可以證明,如果i與j在圓內不能共存,則在圓外也一定不能共存。
即:i內j外,建邊i−>j′; i外j內,建邊i′−>j;
j內i外,建邊j−>i′; j外i內,建邊j′−>i。
*/void reads(int &x)
while(s>='
0'&&s<='9')
x*=f; //
正負號}
const
int n=5019,m=500019
;int
n,m,dfn[n],low[n],stack[n],vis[n];
int dfn_=0,top_=0,colnum=0
,col[n],u[n],v[n];
int head[n],tot=0,rd[n],okk=0
;struct nodee[m];
int xj(int a,int
b)inline
void add(r int x,r int
y)inline
void tarjan(int
x) if(dfn[x]==low[x]) top_--; //
col陣列記錄每個點所在連通塊的編號}}
bool
solve()
intmain()
for(int i=1;i<=m;i++)
for(int j=i+1;j<=m;j++) if
(xj(i,j))
add(i,j+m),add(j,i+m),add(j+m,i),add(i+m,j);
if(solve()) printf("
panda is telling the truth...\n");
else printf("
the evil panda is lying again\n");
}
暖 墟 初級資料結構 分塊
分塊的概念與運用 例題1 單點修改 區間查詢 例題2 區間修改 單點查詢 例題3 區間修改 區間查詢 例題4 bzoj 2724 蒲公英 例題5 bzoj 2038 小z的襪子 通過適當的劃分,預處理一部分資訊並儲存下來。用空間換取時間,達到時空平衡。易實現。將查詢區間分成中間整塊 零散兩邊,此時兩...
資料結構筆記2 資料結構求解問題的過程
1.集合 元素之間的關係 無 特點 資料元素之間 除了屬於同乙個集合 的關係外,別無其他邏輯關係。是最鬆散的,不受任何制約的關係。2.線性關係 元素之間的關係 一對一 特點 開始元素和終端元素都是唯一的,除此之外,其餘元素都有且僅有乙個前趨元素和乙個後繼元素。3.樹形結構 元素之間的關係 一對多 特...
資料結構 2 用棧實現迷宮問題的求解
反思總結 最近看到一句區別棧和佇列很形象的話 棧是先進後出,佇列是先進先出 所以吃多了吐就是棧,吃多了拉就是佇列 如上面的迷宮,用棧實現求迷宮路徑的程式,所求路徑不需要是最短路徑,但必須是簡單路徑,即在求得的路徑上不能重複出現同一通道塊。比如當走到 1,8 時,發現無路可走了,則依次退棧,退到能走通...