SCOI2015 小凸玩矩阵

版权声明:随意转载,愿意的话提一句作者就好了 https://blog.csdn.net/stone41123/article/details/84134317

Link

Diffculty

算法难度6,思维难度5,代码难度6

Description

给定一个 N × M N\times M 的矩阵,要求你每行选一个元素,同时不能有同列元素同时被选。

你要最小化这些元素中的第 K K 大权值。

1 K N < M 250 1\le K\le N<M\le 250

Solution

首先我们可以二分答案,这个非常显然。

二分答案之后你要判断是否存在合法方案,考虑网络流。

我们可以行列建点,因为每行每列都最多选一个,那么可以看做匹配。

这样我们直接跑最大流,就可以判断是否存在合法方案了。

判断合法方案也可以用匈牙利跑最大匹配,或许会更快。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=255,M=N*N*10;
int n,m,k,tot,a[N][N],s,t;
int head[M],to[M],Next[M],flow[M];
inline void addedge(int x,int y,int l){
	to[++tot]=y;Next[tot]=head[x];head[x]=tot;flow[tot]=l;
	to[++tot]=x;Next[tot]=head[y];head[y]=tot;flow[tot]=0;
}
int d[M],q[M],cur[M];
inline bool bfs(){
	for(int i=s;i<=t;++i)d[i]=1e9;
	int l=1,r=1;q[1]=s;d[s]=0;
	while(l<=r){
		int x=q[l++];
		for(int i=head[x];~i;i=Next[i]){
			int u=to[i];
			if(flow[i] && d[u]>d[x]+1){
				d[u]=d[x]+1;
				q[++r]=u;
			}
		}
	}
	return d[t]!=1e9;
}
inline int dfs(int x,int a){
	if(x==t || !a)return a;
	int F=0,f;
	for(int &i=cur[x];~i;i=Next[i]){
		int u=to[i];
		if(flow[i] && d[u]==d[x]+1 && (f=dfs(u,min(a,flow[i])))>0){
			flow[i]-=f;
			flow[i^1]+=f;
			F+=f;
			a-=f;
			if(!a)return F;
		}
	}
	return F;
}
inline int dinic(){
	int F=0;
	while(bfs()){
		for(int i=s;i<=t;++i)cur[i]=head[i];
		F+=dfs(s,1e9);
	}
	return F;
}
int main(){
	n=read();m=read();k=read();
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)a[i][j]=read();
	s=0;t=n+m+1;
	int l=1,r=1e9,mid;
	while(l<r){
		mid=(l+r)>>1;
		tot=-1;
		for(int i=s;i<=t;++i)head[i]=-1;
		for(int i=1;i<=n;++i)addedge(s,i,1);
		for(int i=1;i<=m;++i)addedge(n+i,t,1);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				if(a[i][j]<=mid)addedge(i,n+j,1);
		for(int i=s;i<=t;++i)cur[i]=head[i];
		if(dinic()>=n-k+1)r=mid;
		else l=mid+1;
	}
	printf("%d\n",l);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/stone41123/article/details/84134317