您的位置:首页 > 其它

hdu 2236(最大匹配+枚举上下界)

2013-05-31 21:55 302 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2236

思路:

引:为了保证每行每列只取一个元素,我们可以从二分图最大匹配的思想入手,把行和列分别看做二分图左右两部分,i-j的边权就是第i行第j列的元素的值。这样构图之后,求得的二分图最大匹配的4条边就是不在同行或同列的4个元素。

有了这个思想时候,我们只需要再保证4个元素中最大值与最小值之差尽量小就可以了,于是我们可以二分枚举最大值与最小值之差,并枚举边权值的下界,如果枚举到某个边权值的下界时该图存在最大匹配,那么就更新max,否则就更新min。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 111
int map[MAXN][MAXN];
int lx[MAXN],ly[MAXN];
bool mark[MAXN];
int vmax,vmin,MIN,mid,MAX,p,n;
bool flag;

bool dfs(int u){
for(int i=1;i<=n;i++){
if(map[u][i]>=p&&map[u][i]<=p+mid&&!mark[i]){
mark[i]=true;
if(ly[i]==-1||dfs(ly[i])){
ly[i]=u;
lx[u]=i;
return true;
}
}
}
return false;
}

bool Hungry(){
memset(lx,-1,sizeof(lx));
memset(ly,-1,sizeof(ly));
for(int i=1;i<=n;i++){
memset(mark,false,sizeof(mark));
if(!dfs(i))return false;
}
return true;
}
int main(){
int _case;
scanf("%d",&_case);
while(_case--){
scanf("%d",&n);
vmax=-100,vmin=100;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&map[i][j]);
if(map[i][j]<vmin)vmin=map[i][j];
if(map[i][j]>vmax)vmax=map[i][j];
}
}
MAX=vmax-vmin;
MIN=0;
while(true)
{
mid=(MIN+MAX)>>1;
flag=false;
for(p=vmin;p+mid<=vmax;p++){
if(Hungry()){ flag=true;break; }
}
if(flag)MAX=mid;
if(MIN==mid)break;
if(!flag)MIN=mid;
}
printf("%d\n",MAX);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: