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思想);

#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

 

posted @ 2018-05-21 20:25  ckxkexing  阅读(177)  评论(0编辑  收藏  举报