1. 程式人生 > >[CODE FESTIVAL 2016 Exhibition A]Distance Pairs

[CODE FESTIVAL 2016 Exhibition A]Distance Pairs

lse 不錯 esp ont i++ main distance 似的 std

題意:有一個未知的邊權為$1$的圖,給定所有點到$1$的最短路$a_i$和到$2$的最短路$b_i$,問是否存在這樣的圖,如果存在,問圖中最少有多少條邊

先考慮$a_i$,有$a_1=0,a_i\neq0(i\neq1)$,對於一條邊$(x,y)$有$|a_x-a_y|\leq1$,對於任意$x\neq1$,存在$(x,y)$使得$a_x-1=a_y$,對$b_i$也有類似的約束

所以,我們將點$i$作為$(a_i,b_i)$畫在平面上,那麽至少要有兩條邊:第一條連到$(a_i-1,b_i-1)$或$(a_i-1,b_i)$或$(a_i-1,b_i+1)$,第二條連到$(a_i-1,b_i-1)$或$(a_i,b_i-1)$或$(a_i+1,b_i-1)$,如果這時找不到可以連邊的點,那麽就無解了(點$1$不用連第一種邊,點$2$不用連第二種邊)

如果可以連,那麽我們構造出了一個有$2n-2$條邊的圖,顯然這個圖是滿足條件的

但我們要最小化圖中邊數,所以考慮尋找盡可能多的可以共用的邊

以下我們將第一種邊稱為$a$邊,第二種邊稱為$b$邊,共用邊只可能是兩種情況:1.對於點$i$,存在$(a_i-1,b_i-1)$,此時$i$的$a$邊和$b$邊可以共用;2.存在$i,j$使得$a_i-1=a_j,b_i+1=b_j$,此時$i$的$a$邊可以和$j$的$b$邊共用

我們把這$2n-2$條邊看成點,如果兩條邊可以共用,在代表它們的點之間連一條邊,求最大匹配即可,雖然是一般圖,但註意到只有那些滿足$a_i+b_i$相等的點$i$引出的邊才可能共用,所以建出來的圖是這樣的(圖來自官方題解)

技術分享圖片

圖中框代表某個坐標$(a_i,b_i)$上的點,紅點代表$b$邊,藍點代表$a$邊,如果方框內有連邊說明存在$(a_i-1,b_i-1)$

這種分層圖可以從左上往右下貪心選邊求最大匹配,實現時只需維護每個坐標的未匹配點個數即可

總時間復雜度為$O(n\log n)$,感覺這題還是不錯的

#include<stdio.h>
#include<map>
using namespace std;
map<int,map<int,int> >mp,vis;
int a[100010],b[100010];
bool ex(int x,int y){
	return mp.count(x)&&mp[x].count(y);
}
int main(){
	int n,i,j,s,las,now;
	scanf("%d",&n);
	#define wa {puts("-1");return 0;}
	for(i=1;i<=n;i++){
		scanf("%d%d",a+i,b+i);
		if(i!=1&&a[i]==0)wa
		if(i!=2&&b[i]==0)wa
		mp[a[i]][b[i]]++;
	}
	for(i=1;i<=n;i++){
		if(i!=2){
			if(!(ex(a[i]-1,b[i]-1)||ex(a[i],b[i]-1)||ex(a[i]+1,b[i]-1)))wa
		}
		if(i!=1){
			if(!(ex(a[i]-1,b[i]+1)||ex(a[i]-1,b[i])||ex(a[i]-1,b[i]-1)))wa
		}
	}
	s=0;
	for(i=1;i<=n;i++){
		if(!ex(a[i]-1,b[i]+1)&&!vis[a[i]][b[i]]){
			vis[a[i]][b[i]]=1;
			las=0;
			for(j=0;ex(a[i]+j,b[i]-j);j++){
				now=mp[a[i]+j][b[i]-j];
				s+=min(las,now);
				now-=min(las,now);
				if(ex(a[i]+j-1,b[i]-j-1)){
					s+=now;
					now=min(las,mp[a[i]+j][b[i]-j]);
				}else
					now=mp[a[i]+j][b[i]-j];
				las=now;
			}
		}
	}
	printf("%d",2*n-2-s);
}

[CODE FESTIVAL 2016 Exhibition A]Distance Pairs