POJ2723 Get Luffy Out解题报告tarjan+2-SAT+二分
今天看到讲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思想);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
skr