无题Ⅱ,题解

题目:

分析:

  题意非常明白就是选取n个数字保证这些数字的行列不重复,使这些数字的极差最小。

  看到这个数据范围,枚举没得跑了,怎么枚举?首先我们想一想一行一列只选一个,那么每行每列就一定都有选的,而且只有一个,即第i行对应着一个唯一的第j列,那么我们很容易想到匹配,要求有完美匹配,剩下的就是枚举了,枚举也要考虑优化一下,要不然枚举极差100*枚举起点100*枚举行100*dfs100*100,应该是会t掉的,但是我们可以想一想,是不是有单调性,即如果极差是x满足,比它大的都能满足,如果极差是x不满足,比他小的都不满足,这时我们可以考虑二分,log100基本可以不计(肯定没有7),这是复杂度是7*100*100*100*100=700000000,貌似并不能通过此题,别急,慢慢来,我们这样想,如果数据是比较集中的,那么显然有一大堆没有边的枚举,这是,第一个dfs之后就可应return了,而如果数据比较分散,那么有很多极差比较小的枚举前几个就return了,这个的常数甚至不到1/100,所以并不会t掉(这并不是数据水,怎么造也不容易t,当然由于T没有数据范围,所以如果T很大就。。。)

  最后是代码:

  

#include <cstdio>
#include <cstring>
int n;
const int maxn=100+10;
int ed[maxn][maxn];
int jz[maxn][maxn];
int ma[maxn];
bool vis[maxn];
bool Dfs(int x){
    vis[x]=1;
    for(int i=1;i<=n;i++){
        if(vis[ma[i]])
            continue;
        if(ed[x][i])
            if(!ma[i]||Dfs(ma[i])){
                ma[i]=x;
                return 1;
            }
    }
    return 0;
}
bool Pd(int a,int b){
    memset(ed,0,sizeof(ed));
    memset(ma,0,sizeof(ma));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(jz[i][j]<=b&&jz[i][j]>=a)
                ed[i][j]=1;
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(!Dfs(i))
            return 0;
    }
    return 1;
}
bool Cl(int s){
    for(int i=0;i+s<=100;i++)
        if(Pd(i,i+s))
            return 1;
    return 0;
}
int main(){
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&jz[i][j]);
        int l=0,r=100;
        while(l<=r){
            int mid=(l+r)/2;
            if(Cl(mid))
                r=mid-1;
            else
                l=mid+1;
        }
        printf("%d
",l);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wish-all-ac/p/12875391.html