POJ - 2723:Get Luffy Out(2-SAT+二分)

题目大意:

有n对钥匙和m扇门,每个门上有两把锁,打开其中任何一把锁该扇门都会打开。

钥匙总是成对出现,一旦选择一对中的一个钥匙后,该对的另一个钥匙就会消失。并且只有通过前一扇门才可以去往下一扇门。问当前你已经拥有所有钥匙并知道所有门上锁的情况下,最多能到达多少扇门。

解题思路:

首先确定钥匙必须2选1,即对应了2-SAT的模型。那么接下来就是约束条件了,约束条件毫无疑问是用门上的锁来进行的。

假如门上的锁是a b,我们暂且认为 与 a b 对应的是 a‘ b’  ,那么就意味着我们必须选择这两把中的一个钥匙来打开这扇门,这里正着想不是很好想,我们可以理解为如果我们选择了a‘ 这里必须选b  选择了b’就必须选a,然后以此建边,

剩下的就是二分最远能到达的距离,不停的check只能找出最终答案即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int INF=1e9+7;

int n,m,idx[maxn];
vector<int> v[maxn],r[maxn];

struct node
{
    int x,y;
}door[maxn];
int tot,cnt,low[maxn],dfn[maxn],belong[maxn];
bool vis[maxn];
stack<int> s;
void tarjan(int u)
{
    low[u]=dfn[u]=++tot;
    s.push(u),vis[u]=1;
    for(int i=0;i<v[u].size();i++)
    {
        int now=v[u][i];
        if(!dfn[now])
        {
            tarjan(now);
            low[u]=min(low[u],low[now]);
        }
        else if(vis[now])
            low[u]=min(low[u],dfn[now]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;
        while(!s.empty())
        {
            int now=s.top();
            vis[now]=0,s.pop();
            belong[now]=cnt;
            if(now==u) break;
        }
    }
}

void add(int x,int y) {  v[x].push_back(y); }

int opp[maxn],sl[maxn],color[maxn];

void init()
{
    tot=0,cnt=0;
    for(int i=0;i<=2*n;i++) v[i].clear(),r[i].clear();
    for(int i=0;i<=2*n;i++) low[i]=dfn[i]=belong[i]=0;
    for(int i=0;i<=2*n;i++) sl[i]=0,vis[i]=0;
}
bool judge()
{
    for(int i=1;i<=n;i++)
        if(belong[i]==belong[i+n]) return 0;
    return 1;
}
bool check(int num)
{
    init();
    for(int i=1;i<=num;i++)
    {
        add(idx[opp[door[i].x]],idx[door[i].y]);    //添加约束条件
        add(idx[opp[door[i].y]],idx[door[i].x]);
    }
    for(int i=1;i<=2*n;i++)
    if(!dfn[i]) tarjan(i);
    if(judge()) return 1;   //判断是否合法
    return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<=2*n;i++) opp[i]=0;
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x++,y++;
            opp[x]=y,opp[y]=x;  //把每对钥匙标记为成对
            idx[x]=i,idx[y]=i+n;
        }
        for(int i=1;i<=m;i++) scanf("%d%d",&door[i].x,&door[i].y),door[i].x++,door[i].y++;
        int L=0,R=m,ans=0;
        while(L<=R) //二分最远距离
        {
            int mid=(L+R)>>1;
            if(check(mid)) ans=mid,L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",ans);
    //system("pause");
    }
}

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/81108936
今日推荐