hdu2236(二分法)

无题II

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 798    Accepted Submission(s): 383


Problem Description
这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小。
 

Input
输入一个整数T表示T组数据。
对于每组数据第一行输入一个正整数n(1<=n<=100)表示矩阵的大小。
接着输入n行,每行n个数x(0<=x<=100)。
 

Output
对于每组数据输出一个数表示最小差值。
 

Sample Input
  
  
1 4 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4
 

Sample Output
  
  
3
 

Author
xhd
 
找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小
有不同行不同列可知需要构建二分图,行号和列号分别作为二分图的两部分顶点
难点在于使n个数的最大值与最小值之差最小,感觉无从下手,看了大牛的结解题报告后恍然大悟,可以用枚举法差,差必定在0与矩阵中最小值与最大值之间,即0<=ans<=Max-Min,枚举此区间的所有可能值(其实在做的时候不需要枚举所有可能值,直接二分区间,二分查找枚举ans,既快又准),若无法找到符合条件的匹配,说明ans太小,下界上移,否则上界下移,最终找到的就是ans=上界
 
#include<iostream>
#include<cstdio>
using namespace std;

int def,mid;
const int MAXN=110;
int n;//u,v数目
int g[MAXN][MAXN];
int linker[MAXN];
bool visited[MAXN];

bool dfs(int u)//从左边开始找增广路径
{
    int v;
    for(v=0;v<n;v++)//这个顶点编号从0开始,若要从1开始需要修改
      if(g[u][v]>=def&&g[u][v]<=def+mid&&!visited[v])
      {
          visited[v]=true;
          if(linker[v]==-1||dfs(linker[v]))
          {//找增广路,反向
              linker[v]=u;
              return true;
          }
      }
    return false;//这个不要忘了,经常忘记这句
}

bool hungary()
{
    int res=0;
    int u;
    memset(linker,-1,sizeof(linker));
    for(u=0;u<n;u++)
    {
        memset(visited,0,sizeof(visited));
        if(!dfs(u)) 
			return false;
    }
    return true;
}


int main()
{
	int i,j,cas,Min,Max;
	cin>>cas;
	while(cas--)
	{
		scanf("%d",&n);
		Min=0x3f3f3f3f;
		Max=-1;
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
			{
				scanf("%d",&g[i][j]);
				if(Min>g[i][j])
					Min=g[i][j];
				if(Max<g[i][j])
					Max=g[i][j];
			}
		}
		int high,low;
		high=Max-Min,low=0;
		bool flag;
		while(1)
		{
			mid=(high+low)/2;
			flag=false;
			for(def=Min;def+mid<=Max;def++)
			{
				if(hungary())
				{
					flag=true;
					break;
				}
			}
			if(low==mid)
				break;
			if(!flag)
				low=mid;
			else 
				high=mid;
		}
		printf("%d\n",high);
	}
	return 0;
}




 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值