• poj-2749(2-sat+二分)


    题目链接:http://poj.org/problem?id=2749

    题意:有N个牛棚,牛棚的牛想串门,必须两两牛棚互通,吝啬的农场主不愿建N*(N-1)/2道路,他建了两个相连的中转站s1,s2,每个牛棚建一条路到其中一个中转站,但是有的牛互相憎恨,就不能建在同一个中转站,而有的牛互相喜欢,必须建在同一中转站。问是否可以建出这样的道路,不可以输出-1,可以输出两两牛棚最小的最大曼哈顿距离。

    思路:

    将两个中转站看成问题关键,看成矛盾点,一个牛棚不可以连到两个中转站。

    2-sat主要是建图:

    我们将 i表示连接到s1,i+n(后面用i'表示)表示连接到s2。

    而互相憎恨的牛不能连相同中转站例如: 两个牛棚为 i,j  连接方式:i->j'  ,j->i' ,i'->j ,j'->i;

    互相喜欢的 : i->j , j->i ,i'->j' ,j'->i';

    每种情况都要表示出来。

    之后用二分最小的最大曼哈顿距离。怎么判断呢?

    将连接方式当成限制条件。两两有四种连接情况(当然我们只指出不能成立的情况,及大于二分的距离,如果成立还要限制条件干嘛):

    1.i,j连向s1  dis(i,s1)+dis(j,s1)>d   d为二分距离

      连接方式: i->j'  j->i'  (很显然的意思,i,j都连接s1是不可能的,那当然 当i连接s1是,j必须连接s2了,j也一样)

    2.i,j连向s2  dis(i,s2)+dis(j,s2)>d

      连接方式:i'->j  j'->i

    3.i连接s1,j连接s2  dis(i,s1)+dis(j,s2)+dis(s1,s2)>d  (一定记得当连接不同时,要加s1到s2的距离)

      连接方式:i->j  j'->i'

    4.i连接s2,j连接s1  dis(i,s2)+dis(j,s1)+dis(s1,s2)>d

      连接方式: i'->j' j->i

    之后只要正常判断2-sat是否可行即可。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=5000;
    const int maxm=2000050;
    struct edge{
        int u,v,w,next;
    }e[maxm];
    struct point{
        int x,y,id;
    }p[maxn],s1,s2;
    int h[maxn],dfn[maxn],low[maxn];
    int st[maxn],vis[maxn],belong[maxn];
    int n,m,k,cnt,tot,top,num,dis,dis1[maxn],dis2[maxn];
    int ax[maxn],ay[maxn],bx[maxn],by[maxn];
    
    void init()
    {
        memset(h,-1,sizeof(h));
        memset(dfn,0,sizeof(dfn));
        memset(vis,0,sizeof(vis));
        memset(belong,0,sizeof(maxn));
        cnt=tot=top=num=0;
    }
    
    void add(int u,int v)
    {
        e[cnt].u=u;
        e[cnt].v=v;
        e[cnt].next=h[u];
        h[u]=cnt++;
    }
    
    int dist(point a,point b){return abs(a.x-b.x)+abs(a.y-b.y);}
    //曼哈顿距离 
    void tarjan(int u)
    {
        low[u]=dfn[u]=++tot;
        vis[u]=1;
        st[++top]=u;
        for(int i=h[u];i!=-1;i=e[i].next)
        {
            int v=e[i].v;
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(vis[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            int t;
            num++;
            do{
                t=st[top--];
                vis[t]=0;
                belong[t]=num;
            }while(t!=u);
        }
    }
    
    bool jug(int x)
    {
        init();//记得每次二分都要初始化 
        for(int i=1;i<=n;i++)//连接限制条件 
        {
            for(int j=i+1;j<=n;j++)
            {
                if(dis1[i]+dis1[j]>x)
                    add(i,j+n),add(j,i+n);
                if(dis2[i]+dis2[j]>x)
                    add(i+n,j),add(j+n,i);
                if(dis1[i]+dis2[j]+dis>x)
                    add(i,j),add(j+n,i+n);
                if(dis2[i]+dis1[j]+dis>x)
                    add(i+n,j+n),add(j,i);
            }
        }
        for(int i=0;i<m;i++)//憎恨 
        {
            add(ax[i],ay[i]+n),add(ax[i]+n,ay[i]);
            add(ay[i],ax[i]+n),add(ay[i]+n,ax[i]);
        }
        for(int i=0;i<k;i++)//喜欢 
        {
            add(bx[i],by[i]),add(by[i],bx[i]);
            add(bx[i]+n,by[i]+n),add(by[i]+n,bx[i]+n);
        }
        for(int i=1;i<=2*n;i++)
            if(!dfn[i])
                tarjan(i);
        for(int i=1;i<=n;i++)//判断 
            if(belong[i]==belong[i+n])
                return false;
        return true;
    }
    
    int main()
    {
        std::ios::sync_with_stdio(false);
        while(cin>>n>>m>>k)
        {
            int u,v;
            int maxx=0;
            cin>>s1.x>>s1.y>>s2.x>>s2.y;
            for(int i=1;i<=n;i++)
            {
                cin>>p[i].x>>p[i].y;
                dis1[i]=dist(p[i],s1);//记录到s1的距离 
                dis2[i]=dist(p[i],s2);//记录到s2的距离 
                maxx=max(maxx,max(dis1[i],dis2[i]));
            }
            dis=dist(s1,s2);
            for(int i=0;i<m;i++)//互相憎恨 
                cin>>ax[i]>>ay[i];
            for(int i=0;i<k;i++)//互相喜欢 
                cin>>bx[i]>>by[i];
            int l=0,r=maxx*2+dis,ans=-1;
            while(l<=r)//二分 
            {
                int mid=(l+r)/2;
                if(jug(mid))
                {
                    ans=mid;
                    r=mid-1;
                }
                else
                    l=mid+1;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
  • 相关阅读:
    Mysql case when 根据数字排序 返回 string类型问题
    .NET Core 面试题
    一个人如何完成一整个网站的开发(推荐好文,看完绝对让你回味无穷)转载
    (转)实现C#中等价于的Javascript中的Math.Random()的函数,以得到一个随机数,double类型的,大于0小于1的,17位精度的
    brew安装指定版本的软件
    服务器被植入挖矿木马的心酸过程
    我把双系统的win10抹除了现在开机只按option还是会出现双系统选择,怎么把那个win10给取消了或删除掉
    java.lang.Exception: The server rejected the connection: None of the protocols were accepted
    Linux 技巧:让进程在后台可靠运行的几种方法
    jenkins backup and migration
  • 原文地址:https://www.cnblogs.com/xiongtao/p/10631516.html
Copyright © 2020-2023  润新知