1. 程式人生 > >poj2723 Get Luffy Out解題報告tarjan+2-SAT+二分

poj2723 Get Luffy Out解題報告tarjan+2-SAT+二分

fine pan tor build push 若是 註意 ios ack

今天看到講2-SAT比較好的blog,感覺微微的理解了2-SAT

傳送門

參考: https://blog.csdn.net/leolin_/article/details/6680144

題意:你有2*n把鑰匙,但是在每一對鑰匙中,用了a,就不能用b。你要用這麽多鑰匙去開盡可能多的門。開門的規則是:每個門對於兩把鑰匙,用一把去開就ok。

註意2的10次方一點都不大~~1000。

思路:註意開門是有順序的,所以可以二分答案,每次用mid值規定的門數去建一個圖,跑一邊tarjan縮點,再check一下i和i+2*n,若是同一個縮點塊中,則false(2-sat思想);

技術分享圖片
#include <iostream>
#include 
<cstdio> #include <algorithm> #include <cstring> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <list> #include <iterator> using namespace std; #define pb push_back const
int maxn = 5000+9; int n,m; int key[maxn][2],locks[maxn][2]; int low[maxn],dfn[maxn],vis[maxn],belong[maxn],scc,tot; stack<int>s; vector<int>mp[maxn]; void tarjan(int x) { dfn[x] = low[x] = ++tot; s.push(x);vis[x] = 1; for(int i=0; i<mp[x].size(); i++) { int v = mp[x][i];
if(dfn[v]==0) { tarjan(v); low[x] = min(low[x],low[v]); } else if(vis[v]) { low[x] = min(low[x],dfn[v]); } } if(low[x]==dfn[x]) { scc++; while(1) { int tmp = s.top(); s.pop(); vis[tmp] = 0; belong[tmp] = scc; if(tmp==x)break; } } } void init() { for(int i=1; i<=4*n; i++)mp[i].clear(); } void build(int x) { init(); for(int i=1; i<=n; i++) //因為mp不得不清空.所以連這個也要重新建 { int a = key[i][0],b = key[i][1]; mp[a].pb(b+2*n); mp[b].pb(a+2*n); } for(int i=1; i<=x; i++) { int a = locks[i][0],b = locks[i][1]; mp[a+2*n].pb(b); //對於開門來說,不用這把,就要用另一把鑰匙; if(a!=b)mp[b+2*n].pb(a); } } void ini(){ while(!s.empty())s.pop(); scc = tot = 0; memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); memset(belong,0,sizeof(belong)); } bool check(){ ini(); for(int i=1; i<=4*n; i++) { if(dfn[i]==0)tarjan(i); } for(int i=1; i<=2*n; i++) if(belong[i]==belong[i+2*n]) return false; return true; } int main(){ while(~scanf("%d%d", &n, &m)&&n+m) { for(int i=1; i<=n; i++) { scanf("%d%d",&key[i][0],&key[i][1]); key[i][0]++,key[i][1]++; } for(int i=1; i<=m; i++) { scanf("%d%d", &locks[i][0], &locks[i][1]); locks[i][0]++,locks[i][1]++; } int le = 1,ri = m; //二分範圍不要亂寫! int ans; while(le<=ri) { int mid = (le + ri)>>1; build(mid); if(check()) { ans = mid; le = mid + 1; } else ri = mid - 1; } printf("%d\n",ans); } return 0; }
View Code

poj2723 Get Luffy Out解題報告tarjan+2-SAT+二分