zoukankan      html  css  js  c++  java
  • TTTTTTTTTTTTTTTT POJ 2723 楼层里救朋友 2-SAT+二分

    Get Luffy Out
    Time Limit: 2000MS   Memory Limit: 65536K
    Total Submissions: 8211   Accepted: 3162

    Description

    Ratish is a young man who always dreams of being a hero. One day his friend Luffy was caught by Pirate Arlong. Ratish set off at once to Arlong's island. When he got there, he found the secret place where his friend was kept, but he could not go straight in. He saw a large door in front of him and two locks in the door. Beside the large door, he found a strange rock, on which there were some odd words. The sentences were encrypted. But that was easy for Ratish, an amateur cryptographer. After decrypting all the sentences, Ratish knew the following facts: 

    Behind the large door, there is a nesting prison, which consists of M floors. Each floor except the deepest one has a door leading to the next floor, and there are two locks in each of these doors. Ratish can pass through a door if he opens either of the two locks in it. There are 2N different types of locks in all. The same type of locks may appear in different doors, and a door may have two locks of the same type. There is only one key that can unlock one type of lock, so there are 2N keys for all the 2N types of locks. These 2N keys were divided into N pairs, and once one key in a pair is used, the other key will disappear and never show up again. 

    Later, Ratish found N pairs of keys under the rock and a piece of paper recording exactly what kinds of locks are in the M doors. But Ratish doesn't know which floor Luffy is held, so he has to open as many doors as possible. Can you help him to choose N keys to open the maximum number of doors?

    Input

    There are several test cases. Every test case starts with a line containing two positive integers N (1 <= N <= 210) and M (1 <= M <= 211) separated by a space, the first integer represents the number of types of keys and the second integer represents the number of doors. The 2N keys are numbered 0, 1, 2, ..., 2N - 1. Each of the following N lines contains two different integers, which are the numbers of two keys in a pair. After that, each of the following M lines contains two integers, which are the numbers of two keys corresponding to the two locks in a door. You should note that the doors are given in the same order that Ratish will meet. A test case with N = M = 0 ends the input, and should not be processed.

    Output

    For each test case, output one line containing an integer, which is the maximum number of doors Ratish can open.

    Sample Input

    3 6
    0 3
    1 2
    4 5
    0 1
    0 2
    4 1
    4 2
    3 5
    2 2
    0 0
    

    Sample Output

    4

    题意:有m层楼,从一层到m层,要进入每层都要打开位于该层的两道门中的至少一道。门锁有2n种,每个门锁为2n种中的一种,可以重复。有2n把钥匙,分别对应2n种锁,但是钥匙两两一组,共n组,每组只能选一个来开门,被选中的可以多次使用,另一个一次都不能用。问最多能上多少层。
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <map>
    #include <algorithm>
    #include <set>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long Ull;
    #define MM(a,b) memset(a,b,sizeof(a));
    const double eps = 1e-10;
    const int  inf =0x7f7f7f7f;
    const double pi=acos(-1);
    const int maxn=100+1000;
    
    int n,m,pre[2*maxn],lowlink[2*maxn],dfs_clock,sccno[2*maxn],scc_cnt;
    int door[2*maxn][3],key[2*maxn];
    
    vector<int> G[2*maxn];
    stack<int> S;
    
    void readkey()
    {
       int u,v;
       scanf("%d %d",&u,&v);
       key[u]=v;
       key[v]=u;
    }
    
    void readdoor(int i)
    {
       int u,v;
       scanf("%d %d",&u,&v);
       door[i][0]=u;
       door[i][1]=v;
    }
    
    void tarjan(int u)
    {
        pre[u]=lowlink[u]=++dfs_clock;
        S.push(u);
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(pre[v]==-1)
                {
                    tarjan(v);
                    lowlink[u]=min(lowlink[u],lowlink[v]);
                }
            else if(!sccno[v])
                    lowlink[u]=min(lowlink[u],pre[v]);
        }
    
        if(lowlink[u]==pre[u])
        {
            scc_cnt++;
            while(1)
            {
                int x=S.top();S.pop();
                sccno[x]=scc_cnt;
                if(x==u) break;//找到了当前强连通的起始节点就退出,<br>//不然会破坏其他强连通分量
            }
        }
    }
    
    void find_scc()
    {
        MM(pre,-1);
        MM(sccno,0);
        MM(lowlink,0);
        scc_cnt=dfs_clock=0;
        for(int i=0;i<2*n;i++)
            if(pre[i]==-1)
                tarjan(i);
    }
    
    bool ok(int mid)
    {
        for(int i=0;i<2*n;i++)
            {G[i].clear();}
    
        for(int i=1;i<=mid;i++)
        {
            int a=door[i][0],b=door[i][1];
            G[key[a]].push_back(b);
            G[key[b]].push_back(a);
        }//建图,预先记录门与钥匙
    
        find_scc();
        for(int i=0;i<2*n;i++)
            if(sccno[i]==sccno[key[i]])
             return false;
        return true;
    }
    
    int main()
    {
        while(~scanf("%d %d",&n,&m)&&(n||m))
        {
            MM(key,0);MM(door,0);
            for(int i=1;i<=n;i++)
                    readkey();
            for(int i=1;i<=m;i++)
                    readdoor(i);
    
            int l=0,r=m+1;
            while(r-l>1)
            {
               int mid=(l+r)>>1;
               if(ok(mid))  l=mid;
               else r=mid;
            }
            printf("%d
    ",l);
        }
        return 0;
    }
    

      分析:经典题型,建图不再是以前的那种一个点对应两个(0,1)状态,而是一组数对应两个状态(只能取其中两个中的一个)

    建图核心思路:对于确切的层数,对于一组数,设其中一个为x,则对应的另一个为f[x],如果一个门对应的数值为x,y两个,那么可以确定的是如果选f[x]的话,为了打开这门,必须选y,同理选f[y]的话,必须选x;

    建图即可;再二分查找即可,tarjan求强连通复杂度(n+m)

    易错点:

    1,数组要开两倍,因为输入的n是组数;

  • 相关阅读:
    Linux命令 比较文件
    Linux命令 查看及修改文件属性
    学习初期 荆棘之路
    Linux命令 查看文件内容
    正则表达式中常用的元符号
    InstallShield 工程类型installscript,如何覆盖安装?
    InstallShield 工程类型MSI 使用过程中碰到的问题及解决方法。
    临时保存
    CorelDRAW 文件实用工具 CDRTools 2
    购买 CDRTools 2 正式版
  • 原文地址:https://www.cnblogs.com/smilesundream/p/5483915.html
Copyright © 2011-2022 走看看