學了一天2-sat
從不會到尋找罪犯,感覺成就感滿滿
順便還嘴巴ac了 noi2017遊戲 (雖然這就是一道水題,真的是水題)
uoj210傳送門
題面me就不貼了
要看的話可以戳傳送門
emmmm網上的做法為什麼都那麼簡單啊woc?感覺全世界就me建邊最多
這道題的限制條件還是很清晰了:
1. 一句話,不是真的就是假的
2. 乙個人,不是真人(好人)就是假人(壞人)
3. 乙個人如果是假人,那麼至多有一句話是假的
然而發現這第3個條件十分坑,資料規模太大,直接建邊是會**的…
於是對於每個人,再定義乙個字首真話,表示這個人的 這句話以及這句話之前的所有話中,是否全是真話
建邊很好想的,畫個圖梳理邏輯結構(圖me會附在後面),然後建好了跑就是。
最後輸出方案的時候,選擇所在強連通分量編號較小的那個即可。關於正確性:如果a和!a在不同的圖裡面,那麼選誰都是一樣的。如果a和!a在同一張圖里,就需要選拓撲序靠後的,而先dfs到的強連通分量,深度更深,拓撲序也相對靠後。
#include
#include
#include
using
namespace
std ;
int n , m , tp , id_c , head[600007] ;
int persont[100005] , personf[100005] , provt[100005] , provf[100005] ;
int pret[100005] , pref[100005] , las[100005] , oppo[600005] ;
struct pathp[100000*10 + 100000*2 + 5];
void gg()
void in( int t1 , int t2 )
int sta[600005] , topp ;
int dfn[600005] , dfs_c , scc[600005] , scc_cnt ;
int dfs( int u )
if( lowu == dfn[u] )
} return lowu ;
}bool choose[600005] ;
void solve()
int cnt = 0 ;
for( int i = 1 ; i <= n ; i ++ )
if( choose[ scc[personf[i]] ] ) cnt ++ ;
printf( "%d\n" , cnt ) ;
for( int i = 1 ; i <= n ; i ++ )
if( choose[ scc[personf[i]] ] ) printf( "%d " , i ) ;
}void init() for( int i = 1 ; i <= m ; i ++ )
}int main() else
in( pret[i] , provt[i] ) ;//字首和當前證詞的關係
in( provf[i] , pref[i] ) ;
if( las[x] )
las[x] = i ;
}for( int i = 1 ; i <= n ; i ++ )
if( las[i] )
solve() ;
}
附錄1:邏輯關係圖
(upd 2018.7.10:貌似有個箭頭反了….
箭頭表示推導關係,直接按照這個關係建圖即可
最後乙個供詞字首 和 其他供詞字首 實際上是連起來的(畫圖太難用了,所以me沒有畫出來)
me感覺2-sat的題都可以通過畫這樣的圖的關係來理一理思路,但是在畫圖的時候一定要考慮到所有情況!
UOJ 210 UER 6 尋找罪犯
有n個人分為好人和壞人,說了m句話。好人不會說假話,壞人至多說一句謊話。求出一組解,滿足要求。利用2 sat拆點,乙個人拆成兩個點,表示他是好人和壞人。然而這樣的話邊數是m 2的,所以用前 字尾和優化構圖即可。1 include 2 using namespace std 34 const int ...
210 UER 6 尋找罪犯
開始就感覺是 二分圖 然後就棄t1後就一直想一直想 然而也沒想出來 暴力都沒法打啊.題解確實是二分圖 演算法四的優化也是非常神 神的到現在也沒看懂.60 include include include include includeusing namespace std const int n 80...
UOJ 19 尋找道路
在有向圖 g g g 中,每條邊的長度均為 1 1 1,現給定起點和終點,請你在圖中找一條從起點到終點的路徑,該路徑滿足以下條件 路徑上的所有點的出邊所指向的點都直接或間接與終點連通。在滿足條件 1 的情況下使路徑最短。注意 圖 g g g 中可能存在重邊和自環,題目保證終點沒有出邊。請你輸出符合條...