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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2236

思路:

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

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

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 #define MAXN 111
 8 int map[MAXN][MAXN];
 9 int lx[MAXN],ly[MAXN];
10 bool mark[MAXN];
11 int vmax,vmin,MIN,mid,MAX,p,n;
12 bool flag;
13 
14 bool dfs(int u){
15    for(int i=1;i<=n;i++){
16       if(map[u][i]>=p&&map[u][i]<=p+mid&&!mark[i]){
17          mark[i]=true;
18          if(ly[i]==-1||dfs(ly[i])){
19             ly[i]=u;
20             lx[u]=i;
21             return true;
22          }
23       }
24    }
25    return false;
26 }
27 
28 bool Hungry(){
29    memset(lx,-1,sizeof(lx));
30    memset(ly,-1,sizeof(ly));
31    for(int i=1;i<=n;i++){
32       memset(mark,false,sizeof(mark));
33       if(!dfs(i))return false;
34    }
35    return true;
36 }
37 int main(){
38    int _case;
39    scanf("%d",&_case);
40    while(_case--){
41       scanf("%d",&n);
42       vmax=-100,vmin=100;
43       for(int i=1;i<=n;i++){
44          for(int j=1;j<=n;j++){
45             scanf("%d",&map[i][j]);
46             if(map[i][j]<vmin)vmin=map[i][j];
47             if(map[i][j]>vmax)vmax=map[i][j];
48          }
49       }
50       MAX=vmax-vmin;
51       MIN=0;
52       while(true)
53       {
54          mid=(MIN+MAX)>>1;
55          flag=false;
56          for(p=vmin;p+mid<=vmax;p++){
57             if(Hungry()){ flag=true;break; }
58          }
59          if(flag)MAX=mid;
60          if(MIN==mid)break;
61          if(!flag)MIN=mid;
62       }
63       printf("%d\n",MAX);
64    }
65    return 0;
66 }
View Code

 

 

posted @ 2013-05-31 21:55  ihge2k  阅读(529)  评论(0编辑  收藏  举报