POJ-2749:Building roads(2SAT+二分)

2-SAT的最后一道题目,至此2-SAT就算暂时完结了吧。

这个算法真的是一种很神奇的算法,感觉在某些情况下异常好用。

希望比赛碰到题目可以做出来= = 

题目大意:

有n个牛舍,现在要在地图上修建两个中转站s1 s2。每个牛舍必须修一条到s1或者s2的道路。其中一些奶牛有仇恨,不能修在同一中转站,一些奶牛是friends,必须修建在同一中转站。问保证在全局最优的情况下,两个牛舍之间最远的距离是多少,注意距离为曼哈顿距离

解题思路:

刚开始完全不知道怎么写,模型是一个很裸的2-SAT模型,但是求取两点间最优情况下的最长路是真心不会。因为2-SAT算法只会输出任意解。但是这个最优情况的最长距离没法确定。

后来看了别人题解发现是直接二分check最长距离。即,如果两个牛舍的距离大于我们当前check的距离,意味着他们两个不能选择他们当前的中转站连接。那么选择相对应的对边即可。最后对于建出的图判断合法性,

Ac代码:

#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,n1,n2;
vector<int> v[maxn],G[maxn];

struct Point
{
    int x,y;
}a[maxn],s1,s2;

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) {  G[x].push_back(y); }
int dis(Point p,Point q)
{
    return abs(p.x-q.x)+abs(p.y-q.y);
}
bool judge()
{
    for(int i=1;i<=n;i++)
    if(belong[i]==belong[i+n]) return 0;
    return 1;
}
bool check(int distance)
{
    tot=0,cnt=0;
    for(int i=0;i<=2*n;i++) vis[i]=dfn[i]=low[i]=belong[i]=0,v[i].clear();
    for(int i=0;i<=2*n;i++) v[i]=G[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            if(dis(a[i],s1)+dis(s1,s1)+dis(s1,a[j])>distance)   //大于distance表示不能选择当前方案
            {
                v[i].push_back(j+n);
                v[j].push_back(i+n);
            }
            if(dis(a[i],s1)+dis(s1,s2)+dis(s2,a[j])>distance)
            {
                v[i].push_back(j);
                v[j+n].push_back(i+n);
            }
            if(dis(a[i],s2)+dis(s2,s1)+dis(s1,a[j])>distance)
            {
                v[i+n].push_back(j+n);
                v[j].push_back(i);
            }
            if(dis(a[i],s2)+dis(s2,s2)+dis(s2,a[j])>distance)
            {
                v[i+n].push_back(j);
                v[j+n].push_back(i);
            }
        }
    }
    for(int i=1;i<=2*n;i++)
    if(!dfn[i]) tarjan(i);
    if(judge()) return 1;   //判断合法性
    return 0;
}
int main()
{
    scanf("%d%d%d",&n,&n1,&n2);
    scanf("%d%d%d%d",&s1.x,&s1.y,&s2.x,&s2.y);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    for(int i=1;i<=n1;i++)  //根据约束条件建图
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y+n),add(y,x+n);
        add(x+n,y),add(y+n,x);
    }
    for(int i=1;i<=n2;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(x+n,y+n);
        add(y,x),add(y+n,x+n);
    }
    int L=0,R=10000000,res=-1;  //二分最长路
    while(L<=R)
    {
        int mid=(L+R)>>1;
        if(check(mid)) res=mid,R=mid-1;
        else L=mid+1;
    }
    printf("%d\n",res);
    //system("pause");
}

猜你喜欢

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