poj2749 Building roads 2-sat

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

题意:有n个奶牛,每个奶牛有坐标。有两个集散地,给出坐标,每个奶牛要链接到其中一个集散地上,让最远的两个奶牛的距离(曼哈顿距离)最近。而且有的奶牛间互相讨厌,不能连在一个集散地上,有的互相喜欢,必须连在一个上。

思路:很明显的2-sat,不过还是跑偏了。。首先给出的限制关系还有集散地数量为2都符合2-sat,不过2-sat中没有距离,只是判定。不过只要二分最大值,判断是否可行就可以了。

//开始还以为要让距离和最小,完全读错题了,后来发现了= =


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define Ror(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define mp make_pair
#define pb push_back
#define inf 0x3f3f3f3f

void RI (int& x){
    x = 0;
    char c = getchar ();
    while (c == ' '||c == '\n')    c = getchar ();
    bool flag = 1;
    if (c == '-'){
        flag = 0;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = x * 10 + c - '0';
        c = getchar ();
    }
    if (!flag)    x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}

const int maxn = 1010;
int n,m;
struct Side{
    int v,next;
}side[maxn*maxn];
int top,node[maxn];
int t,dfn[maxn],low[maxn],sum;
stack<int>s;
void add_side(int u,int v){
    side[top] = (Side){v,node[u]};
    node[u] = top++;
}
void init(){
    memset(node,-1,sizeof(node));
    memset(dfn,0,sizeof(dfn));
    top=sum=t=0;
}
void dfs(int u){
    dfn[u]=low[u]=++t;
    s.push(u);
    for(int i=node[u];i!=-1;i=side[i].next){
        int v=side[i].v;
        if(!dfn[v])dfs(v);
        if(dfn[v]!=-1)low[u]=min(low[u],low[v]);
    }
    if(low[u]==dfn[u]){
        int v;
        do{
            v=s.top();s.pop();
            dfn[v]=-1;
            low[v]=sum;
        }while(v!=u);
        sum++;
    }
}
bool solve(){
    for(int i=0;i<n;i++)//判断是否有可行解
        if(low[2*i]==low[2*i+1])return false;
    return true;
}

struct PP{
    int x,y;
}pp[2],cow[maxn],hate[maxn],like[maxn];
int get_dis(PP a,PP b){
    return abs(a.x - b.x) + abs(a.y - b.y);
}
int dis[maxn][2],ddd;
int a,b;

void build(int mid){
    For(i,0,a){
        int a = hate[i].x*2,b = hate[i].y*2;
        int fa = a+1,fb = b+1;
        add_side(a,fb);
        add_side(fa,b);
        add_side(b,fa);
        add_side(fb,a);
    }
    For(i,0,b){
        int a = like[i].x*2,b = like[i].y*2;
        int fa = a+1,fb = b+1;
        add_side(a,b);
        add_side(fa,fb);
        add_side(b,a);
        add_side(fb,fa);
    }
    For(i,0,n){
        For(j,i+1,n){
            For(a,0,2){
                For(b,0,2){
                    if(a == b){
                        if(dis[i][a] + dis[j][b] > mid){
                            add_side(i*2+a,j*2+(!b));
                            add_side(j*2+b,i*2+(!a));
                        }
                    }
                    else {
                        if(dis[i][a] + dis[j][b] + ddd > mid){
                            add_side(i*2+a,j*2+(!b));
                            add_side(j*2+b,i*2+(!a));
                        }
                    }
                }
            }
        }
    }
}
int get_ans(){
    int l = 0,r = 6000010,mid;
    while(l < r){
        init();
        mid = (l + r)>>1;
        build(mid);
        For(i,0,2*n)
            if(!dfn[i])dfs(i);
        if(solve())r = mid;
        else l = mid + 1;
    }
    if(r == 6000010)return -1;
    return r;
}
int main()
{
    //freopen("test.txt","r",stdin);
    while(~scanf("%d%d%d",&n,&a,&b)){
        RII(pp[0].x,pp[0].y);
        RII(pp[1].x,pp[1].y);
        ddd = get_dis(pp[0],pp[1]);
        For(i,0,n){
            RII(cow[i].x,cow[i].y);
        }
        For(i,0,n){
            For(j,0,2){
                dis[i][j] = get_dis(pp[j],cow[i]);
            }
        }
        For(i,0,a){
            RII(hate[i].x,hate[i].y);
            hate[i].x --;
            hate[i].y --;
        }
        For(i,0,b){
            RII(like[i].x,like[i].y);
            like[i].x --;
            like[i].y --;
        }
        int ans = get_ans();
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值