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

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

題目大意

給出nn個穀倉和2箇中轉站的座標,要求每個穀倉都必須連且只連接其中一箇中轉站,有k1k1對穀倉由於某種原因不能連在同一個中轉站,還有k2k2對穀倉由於另某種原因一定要連載同一個中轉站,平面上兩點路徑等於兩點的曼哈頓距離,問所有穀倉連接後所有兩個穀倉的最長路的最小值是多少。

解題分析

“連且只連接其中一箇中轉站”,這明顯是一個2-SAT模型,但2-SAT本身只能求O(n2)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章