1. 程式人生 > >hdu2236無題II(二分圖修改+二分+列舉)

hdu2236無題II(二分圖修改+二分+列舉)

連結:http://acm.hdu.edu.cn/showproblem.php?pid=2236

題目意思:在一個n*n的矩陣中,找n個數,使得這n個數在不同行不同列(類似於八皇后,但是不用管對角線),並且要 求這n個數的最大最小值的差值最小。

題目分析:

        類似於八皇后的不同行不同列一開始我想到了dfs,但是100的資料而且還沒有對角線的剪紙肯定超時吧,23333,所以這題二分圖匹配的話就很巧妙了。為什麼會想到用二分圖呢?仔細想一下,如果某點(x,y),被選中,那麼橫座標上x的值不能再選,縱座標上的y值也不能再選,這相當於二維上有很多值,但是每個值都只能用一次,這就可以想到用二分圖匹配求完美匹配了。

        想到用二分圖匹配後,難點是求n個數的最大最小的差值最小。如果常規的想解決這個問題,無非就是找到每次匹配後的最大最小值,但是存在的一個問題是一般我們只要找到一個最大匹配就行了,找出所有的完美匹配還是困難的呢,各種跡象表明,這樣是會超時的。這裡求差值最小,其實是一個二分答案的套路應用吧:,首先這個差值一定是0-(maxv-minv)之間的,然後我們二分差值為外迴圈,列舉下界為內迴圈,得到一個區間。修改二分圖匹配模板:如果這個二分圖的匹配點是在這個區間裡的前去匹配,hungry()函式中返回是否為完美匹配,對於每一個二分的差值,只要它能找到一個滿足區間條件的完美匹配,就記錄一下答案。

程式碼:

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn =333;
const int inf=0x3f3f3f3f;
int g[maxn][maxn];
int n;
int maxv,minv;
int l,r,mid;

int linker[maxn];
int used[maxn];
int p;

int dfs(int x){
	for(int i=1;i<=n;i++){
		if(p<=g[x][i]&&g[x][i]<=p+mid&&!used[i]){//***界定二分,模板修改核心
			used[i]=1;
			if(linker[i]==-1||dfs(linker[i])){
				linker[i]=x;
				return 1;
			}
		}
	}
	return 0;
}

int hungry(void){
	int res=0;
	memset(linker,-1,sizeof(linker));
	for(int i=1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(dfs(i))res++;
	}
	return res==n?1:0;//***模板修改
}

int main(){
	int t;
	scanf("%d",&t);
	while(t--){

		maxv=-inf;
		minv=inf;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&g[i][j]);
				maxv=max(maxv,g[i][j]);
				minv=min(minv,g[i][j]);
			}
		}
		l=0;
		r=maxv-minv;
		int res=0;
		while(l<=r){
			int f=0;
			mid=(l+r)/2;
			for(p=minv;p+mid<=maxv;p++){//列舉下屆確定範圍
				if(hungry()){
					f=1;
					break;
				}
			}
			if(f){res=mid;r=mid-1;}
			else l=mid+1;
		}
		printf("%d\n",res);
	}
}