无题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)。
对于每组数据第一行输入一个正整数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;
}