[二分+2-SAT]POJ 2749 Building roads 题解

版权声明:本文为博主原创文章,欢迎转载。 https://blog.csdn.net/try__jhf/article/details/82749663

[二分+2-SAT]POJ 2749 Building roads 题解

题目大意

给出 n n 个谷仓和2个中转站的坐标,要求每个谷仓都必须连且只连接其中一个中转站,有 k 1 k1 对谷仓由于某种原因不能连在同一个中转站,还有 k 2 k2 对谷仓由于另某种原因一定要连载同一个中转站,平面上两点路径等于两点的曼哈顿距离,问所有谷仓连接后所有两个谷仓的最长路的最小值是多少。

解题分析

“连且只连接其中一个中转站”,这明显是一个2-SAT模型,但2-SAT本身只能求 O ( n 2 ) O(n^2) 字典序最小解和 O ( n ) O(n) 任意解,无法做到求出在某种方案下的最优解,所以……最大值最小,二分,再用2-SAT,构思一下发现能行,那么就可以二分枚举答案,2-SAT判断答案。那么问题还有一个,就是2-SAT建图的问题,厌恶和喜欢的这两种我们已经讨论过,但是如果枚举了答案ans,那么还有一些关系因为距离过长也要ban掉。一开始预估数组大小和建边要仔细,否则会被卡……

解题报告

(题目传送门)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=505,maxm=1005,maxe=(maxm+maxn*maxn)<<3;
int n,ans,ka,kb,ds,sx[2],sy[2],aa[maxm],ab[maxm],ba[maxm],bb[maxm],ax[maxn],ay[maxn];
int tot,son[maxe],nxt[maxe],lnk[maxm],ti,k,top,low[maxm],dfn[maxm],stk[maxm],SCC[maxm];
bool instk[maxm];
inline void readi(int &x){
	x=0; char ch=getchar(),lst='+';
	while ('0'>ch||ch>'9') {lst=ch; ch=getchar();}
	while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
	if (lst=='-') x=-x;
}
int absi(int x){return x>0?x:-x;}
int getd(int x,int y){return absi(ax[x]-sx[y])+absi(ay[x]-sy[y]);}
void _init(){
	freopen("roads.in","r",stdin);
	freopen("roads.out","w",stdout);
	readi(n); readi(ka); readi(kb);
	readi(sx[0]); readi(sy[0]); readi(sx[1]); readi(sy[1]);
	ds=absi(sx[0]-sx[1])+absi(sy[0]-sy[1]);
	for (int i=0;i<n;i++){readi(ax[i]); readi(ay[i]);}
	for (int i=1;i<=ka;i++){readi(aa[i]); readi(ab[i]); aa[i]--; ab[i]--;}
	for (int i=1;i<=kb;i++){readi(ba[i]); readi(bb[i]); ba[i]--; bb[i]--;}
}
void _add(int x,int y){son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;}
void _build(int x){
	tot=0; memset(lnk,0,sizeof(lnk));
	for (int i=1;i<=ka;i++){ //厌恶关系
		_add(aa[i]<<1,ab[i]<<1^1); _add(ab[i]<<1,aa[i]<<1^1);
		_add(aa[i]<<1^1,ab[i]<<1); _add(ab[i]<<1^1,aa[i]<<1);
	}
	for (int i=1;i<=kb;i++){ //喜欢关系
		_add(ba[i]<<1,bb[i]<<1); _add(ba[i]<<1^1,bb[i]<<1^1);
		_add(bb[i]<<1,ba[i]<<1); _add(bb[i]<<1^1,ba[i]<<1^1);
	}
	for (int i=0;i<n-1;i++)
		for (int j=i+1;j<n;j++){
			if (x<getd(i,0)+getd(j,0)) {_add(i<<1,j<<1^1); _add(j<<1,i<<1^1);}
             //i和j不能都到0
			if (x<getd(i,1)+getd(j,1)) {_add(i<<1^1,j<<1); _add(j<<1^1,i<<1);}
             //i和j不能都到1
			if (x<getd(i,0)+getd(j,1)+ds) {_add(i<<1,j<<1); _add(j<<1^1,i<<1^1);}
             //i到0则j不能到1或j到1则i不能到0
			if (x<getd(i,1)+getd(j,0)+ds) {_add(i<<1^1,j<<1^1); _add(j<<1,i<<1);}
             //i到1则j不能到0或j到0则j不能到1
		}
}
void _dfs(int x){
	dfn[x]=low[x]=++ti; stk[++top]=x; instk[x]=1;
	for (int j=lnk[x];j;j=nxt[j])
		if (!dfn[son[j]]) {_dfs(son[j]); low[x]=min(low[x],low[son[j]]);}
		else if (instk[son[j]]) low[x]=min(low[x],dfn[son[j]]);
	if (dfn[x]==low[x]){
		int y; k++;
		do{y=stk[top--]; instk[y]=0; SCC[y]=k;
		}while(x!=y);
	}
}
bool _check(int x){
	_build(x); ti=top=k=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(instk,0,sizeof(instk));
	for (int i=0;i<n<<1;i++) if (!dfn[i]) _dfs(i);
	for (int i=0;i<n<<1;i+=2) if (SCC[i]==SCC[i^1]) return 0;
	return 1;
}
void _solve(){
	int L=0,R=12000000; ans=-1;
	while (L<=R){
		int mid=(R-L)/2+L;
		if (_check(mid)) {ans=mid; R=mid-1;}
		else L=mid+1;
	}
	printf("%d",ans);
}
int main()
{
	_init();
	_solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/try__jhf/article/details/82749663