刷题总结——小凸玩矩阵(scoi)

题目:

题目背景

SCOI2015 DAY1 T1

题目描述

小凸和小方是好朋友,小方给了小凸一个 n×m(n≤m)的矩阵 A,并且要求小凸从矩阵中选出 n 个数,其中任意两个数都不能在同一行或者同一列。
现在小凸想知道,选出的 n 个数中第 k 大的数的最小值是多少。

输入格式

第 1 行读入 3 个整数 n,m,k。
接下来 n 行,每一行有 m 个数字,第 i 行第 j 个数字代表矩阵中第 i 行第 j 列的元素 Ai,j 。

输出格式

输出包含一行,为选出的 n 个数中第 k 大数的最小值。

样例数据 1

输入  [复制]

 
2 3 1 
1 2 4 
2 4 1

输出

1

样例数据 2

输入  [复制]

 
3 4 2 
1 5 6 6 
8 3 4 3 
6 8 6 3

输出

3

备注

【数据范围】
对于 20% 的数据,1≤n≤m≤9
对于 40% 的数据,1≤n≤m≤22;1≤n≤12
对于 100% 的数据,1≤k≤n≤m≤250;1≤Ai,j≤109


题解:

二分加最大匹配(网络流/匈牙利)算法,枚举已有的数,然后小于该数的连边建图,以匹配数为n-k为标准二分答案即可

心得:

最开始竟然没看出来是二分匹配···哎,从行和列不能重复这个条件明显可以分析出来的··看来对每个条件都要仔细考虑啊···

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=300;
int n,m,map[N][N],first[N],next[N*N],go[N*N],tot=0,maxx=0,belong[N],k,used[N];
inline void comb(int a,int b)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b;
}
inline void clear()
{
  tot=0;
  memset(first,0,sizeof(first));
  memset(belong,0,sizeof(belong));
  memset(used,0,sizeof(used));
}
inline bool find(int u,int T)
{
  for(int e=first[u];e;e=next[e])
  {
    if(used[go[e]]!=T)
    {
      used[go[e]]=T;
      if(!belong[go[e]]||find(belong[go[e]],T))
      {
        belong[go[e]]=u;
        return true;
      }
    } 
  }
  return false;
}
int main()
{
 // freopen("a.in","r",stdin);
  scanf("%d%d%d",&n,&m,&k);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {  
      scanf("%d",&map[i][j]);
      maxx=max(maxx,map[i][j]);
    }
  int l=1,r=maxx;
  while(l<=r)
  {
    clear();
    int mid=(l+r)/2;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        if(map[i][j]<=mid)
          comb(i,j);
    int temp=0;
    for(int i=1;i<=n;i++)      
      if(find(i,i)) temp++;   
    if(temp>=n-k+1)  r=mid-1;
    else l=mid+1;
  }
  cout<<l<<endl;
  return 0;
}
原文地址:https://www.cnblogs.com/AseanA/p/6668630.html