题目链接: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 }