• [洛谷P4251] 小凸玩矩阵


    问题描述

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

    输入格式

    第 1 行读入 3 个整数 n, m, k。

    接下来 n 行,每一行有 m 个数字,第 i 行第 j 个数字代表矩阵中第 i 行第 j 列的元素 (A_{i,j})

    输出格式

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

    数据范围

    对于 20% 的数据, (1 leq n leq m leq 9)

    对于 40% 的数据, (1 leq n leq m leq 22, 1 leq n leq 12)

    对于 100% 的数据, (1 leq k leq n leq m leq 250, 1 leq A_{i,j} leq 10^9)

    链接

    洛谷

    解析

    每一行每一列都只能有一个点被选中,这与二分图匹配的限制十分相似。不妨往这一方面去考虑。

    首先发现答案是具有单调性的,那么先二分一个mid,把矩阵中大于等于mid的数设为1,小于mid的设为0。然后,可以把新产生的矩阵当做一个邻接矩阵,跑二分图最大匹配。如果匹配数量大于等于(n-mid+1),说明能找到(n-mid+1)以上个比(mid)大的满足要求的数,即(mid)偏小;反之偏大。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define N 252
    using namespace std;
    int n,m,k,i,j,g[N][N],a[N][N],match[N*N];
    bool vis[N*N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    bool dfs(int x)
    {
    	for(int i=1;i<=m;i++){
    		if(g[x][i]&&!vis[n+i]){
    			vis[n+i]=1;
    			if(!match[n+i]||dfs(match[n+i])){
    				match[n+i]=x;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    int hungary()
    {
    	int ans=0;
    	for(int i=1;i<=n;i++){
    		if(!match[i]){
    			memset(vis,0,sizeof(vis));
    			if(dfs(i)) ans++;
    		}
    	}
    	return ans;
    }
    bool check(int x)
    {
    	memset(match,0,sizeof(match));
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++) g[i][j]=(a[i][j]<=x);
    	}
    	if(hungary()>=n-k+1) return true;
    	return false;
    }
    int main()
    {
    	int l=1,r=0,mid,ans;
    	n=read();m=read();k=read();
    	for(i=1;i<=n;i++){
    		for(j=1;j<=m;j++){
    			a[i][j]=read();
    			r=max(r,a[i][j]);
    		}
    	}
    	while(l<=r){
    		mid=(l+r)/2;
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    [力扣活动] 914. 卡牌分组
    [ 力扣活动0319 ] 409. 最长回文串
    88. 合并两个有序数组
    自己无聊封装一个 LTView
    ios ViewController 页面跳转
    UI打地鼠
    登陆页面,找回密码,注册页面(三个view的切换)
    登陆页面
    UIView 和Label
    对oracle里面clob字段里面xml的增删改查学习
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12220642.html
Copyright © 2020-2023  润新知