Building roads(POJ 2749)---2-SAT模板题+二分

题目链接

题目描述

Farmer John’s farm has N barns, and there are some cows that live in each barn. The cows like to drop around, so John wants to build some roads to connect these barns. If he builds roads for every pair of different barns, then he must build N * (N - 1) / 2 roads, which is so costly that cheapskate John will never do that, though that’s the best choice for the cows.
Clever John just had another good idea. He first builds two transferring point S1 and S2, and then builds a road connecting S1 and S2 and N roads connecting each barn with S1 or S2, namely every barn will connect with S1 or S2, but not both. So that every pair of barns will be connected by the roads. To make the cows don’t spend too much time while dropping around, John wants to minimize the maximum of distances between every pair of barns.
That’s not the whole story because there is another troublesome problem. The cows of some barns hate each other, and John can’t connect their barns to the same transferring point. The cows of some barns are friends with each other, and John must connect their barns to the same transferring point. What a headache! Now John turns to you for help. Your task is to find a feasible optimal road-building scheme to make the maximum of distances between every pair of barns as short as possible, which means that you must decide which transferring point each barn should connect to.
We have known the coordinates of S1, S2 and the N barns, the pairs of barns in which the cows hate each other, and the pairs of barns in which the cows are friends with each other.
Note that John always builds roads vertically and horizontally, so the length of road between two places is their Manhattan distance. For example, saying two points with coordinates (x1, y1) and (x2, y2), the Manhattan distance between them is |x1 - x2| + |y1 - y2|.

输入格式

The first line of input consists of 3 integers N, A and B (2 <= N <= 500, 0 <= A <= 1000, 0 <= B <= 1000), which are the number of barns, the number of pairs of barns in which the cows hate each other and the number of pairs of barns in which the cows are friends with each other.
Next line contains 4 integer sx1, sy1, sx2, sy2, which are the coordinates of two different transferring point S1 and S2 respectively.
Each of the following N line contains two integer x and y. They are coordinates of the barns from the first barn to the last one.
Each of the following A lines contains two different integers i and j(1 <= i < j <= N), which represent the i-th and j-th barns in which the cows hate each other.
The same pair of barns never appears more than once.
Each of the following B lines contains two different integers i and j(1 <= i < j <= N), which represent the i-th and j-th barns in which the cows are friends with each other. The same pair of barns never appears more than once.
You should note that all the coordinates are in the range [-1000000, 1000000].

输出格式

You just need output a line containing a single integer, which represents the maximum of the distances between every pair of barns, if John selects the optimal road-building scheme. Note if there is no feasible solution, just output -1.

输入样例

4 1 1
12750 28546 15361 32055
6706 3887
10754 8166
12668 19380
15788 16059
3 4
2 3

输出样例

53246

分析

题目大意是一共有n个谷仓和2个中转站(1号、2号),a个互相厌恶的关系以及b个互相喜欢的关系,每个谷仓和中转站都有对应的坐标,现将每个谷仓与中转站连接起来,且每个谷仓只能连接在一个中转站上,具有互相厌恶关系无法连接在一个中转站,具有互相喜欢关系必须连接在一个中转站,求连接完毕之后任意两谷仓的最小距离。

对于这道题,由于谷仓具体连接在哪个中转站是不确定的,所以需要用2-SAT和二分的思想来解,建图的原则是这样的:
对于互相厌恶的:

  • 若a在1,则b必在2,有边:<a,b+n>。
  • 若b在1,则a必在2,有边:<b,a+n>。
  • 若a在2,则b必在1,有边:<a+n,b>。
  • 若b在2,则a必在1,有边:<b+n,a>。

对于互相喜欢的:

  • 若a在1,则b必在1,有边:<a,b>。
  • 若b在1,则a必在1,有边:<b,a>。
  • 若a在2,则b必在2,有边:<a+n,b+n>。
  • 若b在2,则a必在2,有边:<b+n,a+n>。

对于任意一个谷仓a,设a到1的距离为dis1,a到2的距离为dis2,当前二分的距离为mid,则:

  • 若dis1>mid,则a必在2,有边:<a,a+n>。
  • 若dis2>mid,则a必在1,有边:<a+n,a>。

*对于任意两个谷仓a,设a到1的距离为dis1,a到2的距离为dis2,b到1的距离为dis3,b到2的距离为dis4,当前二分的距离为mid,1到2的距离为dis,则:

  • 若dis1+dis4+dis>mid,即无法同时a连接1,b连接2,则有以下两种情况:
    a在1,则b必在1,有边:<a,b>。
    b在2,则a必在2,有边:<b+n,a+b>。
  • 若dis2+dis3+dis>mid,即无法同时a连接2,b连接1,则有以下两种情况:
    a在2,则b必在2,有边:<a+n,b+n>。
    b在1,则a必在1,有边:<b,a>。

综上,按照上述原则每次二分建立图,注意每次建图的时候要将原图还原再添加新的条件,建完图后用tarjan算法求强连通分量,如果存在编号i满足所属强连通分量scc[i]==scc[i+n]则表示矛盾,此时l=mid+1,否则r=mid,以下是源代码。

源程序

//#include <bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#define MAXN 1005
#define MAXM MAXN*MAXN
#define INF 0x3f3f3f3f
using namespace std;
struct Point{
	int x,y;
}point[MAXN];
struct Edge{
	int v,next;
	Edge(){}
	Edge(int v,int next):v(v),next(next){}
}edge[MAXM];
int EdgeCount,head[MAXN];
int sx1,sx2,sy1,sy2,dis,diss[MAXN][2];
int n,a,b,block_cnt,scc_cnt;
int dfn[MAXN],low[MAXN],scc[MAXN];
bool vis[MAXN];
stack<int> s;
void addEdge(int u,int v)
{
	edge[++EdgeCount]=Edge(v,head[u]);
	head[u]=EdgeCount;
}
void init()
{
	memset(head,0,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(scc,0,sizeof(scc));
	memset(vis,false,sizeof(vis));
	EdgeCount=block_cnt=scc_cnt=0;
}
void tarjan(int u)
{
	dfn[u]=low[u]=++block_cnt;	//时间戳 
	vis[u]=true;	//标记入栈 
	s.push(u);
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[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(dfn[u]==low[u]){		//满足强连通分量要求 
		scc_cnt++;
		while(1){
			int tmp=s.top();s.pop();
			scc[tmp]=scc_cnt;	//标记所属强连通分量 
			vis[tmp]=false;	//标记出栈
			if(tmp==u)break; 
		}
	}
}
bool TwoSat()
{
	memset(dfn,0,sizeof(dfn));
	for(int i=1;i<=2*n;i++)	//缩点 
		if(!dfn[i])
			tarjan(i);
	for(int i=1;i<=n;i++)	
		if(scc[i]==scc[i+n])	//矛盾 
			return false;
	return true;
}
void build(int NowEdge,int mid)		//参数NowEdge,保存未建图前的边数 
{									//参数mid,记录的是当前最大值 
	for(int i=1;i<=2*n;i++)	//还原图 
		while(head[i]>NowEdge)
			head[i]=edge[head[i]].next; 
	EdgeCount=NowEdge;
	for(int i=1;i<=n;i++){	
		if(diss[i][0]>mid)	//无法连接在中转1
			addEdge(i,i+n); 
		if(diss[i][1]>mid)	//无法连接在中转2
			addEdge(i+n,n);
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(diss[i][0]+diss[j][0]>mid){	//无法同时连接在中转1
				addEdge(i,j+n);		//i在1,j在2 
				addEdge(j,i+n);		//j在1,i在2
			}
			if(diss[i][1]+diss[j][1]>mid){	//无法同时连接在中转2
				addEdge(i+n,j);		//i在1,j在2 
				addEdge(j+n,i);		//j在1,i在2
			}
			if(diss[i][0]+diss[j][1]+dis>mid){	//无法i连接1,j连接2
				addEdge(i,j);	//i在1,j在1
				addEdge(j+n,i+n);	//i在1,j在1 	 
			}
			if(diss[i][1]+diss[j][0]+dis>mid){	//无法i连接2,j连接1
				addEdge(i+n,j+n);	//i在1,j在1
				addEdge(j,i);	//i在1,j在1 	 
			}
		} 
	}
}
int main()
{
	while(~scanf("%d%d%d",&n,&a,&b)){
		init();
		scanf("%d%d%d%d",&sx1,&sy1,&sx2,&sy2);
		dis=abs(sx2-sx1)+abs(sy2-sy1);	//两中转站间距离 
		for(int i=1;i<=n;i++)
			scanf("%d%d",&point[i].x,&point[i].y);
		for(int i=1;i<=n;i++){	//各点与中转站的距离 
			diss[i][0]=abs(sx1-point[i].x)+abs(sy1-point[i].y);	//与中转1的距离 
			diss[i][1]=abs(sx2-point[i].x)+abs(sy2-point[i].y);	//与中转2的距离
		}
		for(int i=1;i<=a;i++){	//彼此讨厌 
			int x,y;
			scanf("%d%d",&x,&y);
			addEdge(x,y+n);		//x在1号,y在2号 
			addEdge(y,x+n);		//y在1号,x在2号
			addEdge(x+n,y);		//x在2号,y在1号
			addEdge(y+n,x);		//y在2号,x在1号 
		}
		for(int i=1;i<=b;i++){	//彼此喜欢
			int x,y;
			scanf("%d%d",&x,&y);
			addEdge(x,y);		//x在1号,y在1号 
			addEdge(y,x);		//y在1号,x在1号
			addEdge(x+n,y+n);		//x在2号,y在2号
			addEdge(y+n,x+n);		//y在2号,x在2号 	 
		}
		int NowEdge=EdgeCount;
		int l=0,r=INF,flag=0,ans=-INF;
		while(l<r){	//二分最大值 
			int mid=(l+r)>>1;
			build(NowEdge,mid);	//添加谷仓与中转站约束 
			if(TwoSat()){	//存在解 
				r=mid;
				ans=r;
				flag=1;
			} 
			else l=mid+1;
		}
		if(flag)
			printf("%d\n",ans);
		else 
			printf("-1\n"); 
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值