poj2723 2sat判断解+二分

典型的2-sat问题,题意:有m个门,每个门上俩把锁,开启其中一把即可,现在给n对钥匙(所有

钥匙编号0123456...2n-1),每对钥匙只能用一把,要求尽可能开门多(按顺序,前max个)。

关键是题意的分析与转化,只能选一?必然2-sat,每给一对门上的锁对应钥匙的编号,说:必需要这

俩把钥匙的一把(至少),即:a^b,所以,建图了可以,总结通法:对应的数据编号:0123..,每个
数代表原来的一个“状态”/“命题”/“数据”,使之01为一对(只取一个),23一对,...依次,建图
此题要求最值,二分即可。
注意点(WA之因):1.编号后全图全按编号走啊!原来数据基本无用,只是有时候输出时之用,或建立
数据双向关系!2.对于每次二分,对应数要重新建图,注意初始化!

ps:一晚没成功,结果早上起来2分钟,AC!上午效率就是高!切记不可熬夜!身体健康第一位!

#include<iostream>    //36MS
#include<cstring>
#include<cstdio>
#include<stack>
#include<vector>
using namespace std;
const int MAX=3000;
vector<int>keys(MAX);int n,m;int times=0;
int belong[MAX];
int low[MAX];int dfn[MAX];int visited[MAX];int isinstack[MAX];stack<int>s;
int scc[MAX];int numblock=0;
struct request                  //条件
{
    int a,b;
};
request requests[MAX];
vector<vector<int> >edges(MAX);  //图
void clear()
{
    times=numblock=0;
    for(int i=0;i<2*n;i++)
    {
        visited[i]=dfn[i]=low[i]=isinstack[i];
        scc[i]=-1;
        edges[i].clear();
    }
}
void tarjan(int u)       //dfs
{
    dfn[u]=low[u]=++times;
    isinstack[u]=1;
    s.push(u); int len=edges[u].size();
    for(int i=0;i<len;i++)
       {
           int v=edges[u][i];
           if(visited[v]==0)
           {
               visited[v]=1;
               tarjan(v);
               if(low[u]>low[v])low[u]=low[v];
           }
           else if(isinstack[v]&&dfn[v]<low[u])
              low[u]=dfn[v];
       }
    if(low[u]==dfn[u])
    {
        numblock++;int cur;
        do
        {
            cur=s.top();s.pop();
            isinstack[cur]=0;
            scc[cur]=numblock;
        }while(cur!=u);
    }
}
bool check(int maxnum)           //检查
{
     clear();
    for(int i=0;i<maxnum;i++)     //这里的数据的转化
    {
        if(requests[i].a==requests[i].b)
           {
               edges[belong[requests[i].a]^1].push_back(belong[requests[i].b]);
           }
      else
      {
       edges[belong[requests[i].a]^1].push_back(belong[requests[i].b]);
       edges[belong[requests[i].b]^1].push_back(belong[requests[i].a]);
      }
    }
    for(int i=0;i<2*n;i++)
         if(visited[i]==0)
         {
             visited[i]=1;
             tarjan(i);
         }
    for(int i=0;i<2*n;i+=2)
        if(scc[i]==scc[i+1])         //因为这里跪了半天!注意编号!图顶点用的都是编号!
                return 0;
     return 1;
}
void readin()
{
    for(int i=0;i<2*n;i++)
    {
        scanf("%d",&keys[i]);
        belong[keys[i]]=i;
    }
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&requests[i].a,&requests[i].b);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)&&(n||m))
    {
        readin();
        int left=0,right=m,mid;
        int count=-1;
        while(right>left)       //二分之
        {
            mid=(left+right)/2;
            if(mid==count&&mid==left)   //注意出口!
            {
                if(check(left+1))left++;
                break;
            }
            if(check(mid))
                left=mid;
            else right=mid;
           count=mid;
         }
        printf("%d
",left);
    }
}


原文地址:https://www.cnblogs.com/yezekun/p/3925811.html