LINK

2021牛客暑期多校训练营3 C.Minimum grid(二分图匹配)_c++

最差情况下答案是 ∑ i = 1 n a i + ∑ i = 1 n b i \sum\limits_{i=1}^n a_i+\sum\limits_{i=1}^nb_i i=1nai+i=1nbi

考虑减小答案的唯一方式是在格子 ( i , j ) (i,j) (i,j)放下数字且 a i a_i ai,且此时 a i = = b j a_i==b_j ai==bj就同时满足一行一列

这样就省去了一个 a i a_i ai的代价

于是考虑单独提出权值为 w w w的行和列,问题转化为一个匹配问题

若格子 ( i , j ) (i,j) (i,j)存在可以由 i i i行向 j j j列连边,在这个位置填充数字 w w w可以省掉 w w w的费用

求一个最大匹配即可

对于每种权值都这样做一遍,由于保证没有一种权值出现次数多于 500 500 500,这保证了二分图的边不会很多

所以复杂度是对的

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3+10;
int n,m,k,girl[2009],used[2002];
bool vis[maxn][maxn];
vector<int>vec[maxn],row[maxn],col[maxn];
bool find(int u)
{
	for(auto v:vec[u] )
	{
		if( used[v] )	continue;
		used[v] = 1;
		if( girl[v]==0 || find( girl[v] ) )
		{
			girl[v] = u;
			return true;	
		} 
	}
	return false;
}
int main()
{
	cin >> n >> m >> k;
	for(int i=1;i<=n;i++)
	{
		int x; scanf("%d",&x );
		row[x].push_back( i );
	}
	for(int i=1;i<=n;i++)	
	{
		int x; scanf("%d",&x );
		col[x].push_back( i );
	}
	for(int i=1;i<=m;i++)
	{
		int l,r; scanf("%d%d",&l,&r );
		vis[l][r] = 1;
	}
	long long ans = 0;
	for(int w=k;w>=1;w--)//枚举每一种权值,那么对应的行和列 
	{
		if( row[w].size()==0 && col[w].size()==0 )	continue;
		for(auto u:row[w] )
		for(auto v:col[w] )
		{
			if( vis[u][v] )
				vec[u].push_back( v );//u->v
		}
		int mat = 0;
		for(auto u:row[w] )
		{
			for(auto v:col[w] )	used[v] = 0;
			if( find(u) )	mat++;
		}
		for(auto v:col[w] )	girl[v] = 0;
		ans += 1ll*( row[w].size()+col[w].size()-mat )*w;
		for(auto u:row[w] )	vec[u].clear();
	} 
	cout << ans;
}