2021牛客暑期多校训练营3 C.Minimum grid (思维,二分图最大匹配)
-
题意:有一个\(n\)x\(n\)的矩阵,有\(m\)个单位有权值,其他单位均为空,给你两个数组\(a\)和\(b\),\(a[i]\)表示第\(i\)行的最大元素,\(b[i]\)表示第\(i\)列的最大元素,你需要在这\(m\)个单位中填值,在满足\(a\)和\(b\)数组的条件下求矩阵最小\(sum\).
-
题解:将最大值为\(k\)的\(a[i]\)和\(b[i]\)拿出来单独看,很明显,如果行和列相交,那么将这个点填\(k\)可以同时满足一行和一列,所以我们要尽可能的填这些相交的点.那么经典套路,将行和列抽象成点,\(m\)个单位有权值,也就是\(m\)条边,然后去枚举\(k\),权值为\(k\)的行和列可以形成一张二分图,行在左边,列在右边,求行和列的最多相交点,就是求二分图的最大匹配.因为题目说了\(a\)的\(b\)的权值数最大\(500\)个,\(4000\)个点,用匈牙利算法是妥妥的能跑过去的.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,m,k; int a[N],b[N]; vector<int> row[N],col[N]; int vis[4000][4000]; vector<int> edge[N]; bool st[4000]; int match[N]; bool find(int u){ for(auto w:edge[u]){ if(!st[w]){ st[w]=true; if(match[w]==0 || find(match[w])){ match[w]=u; return true; } } } return false; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>m>>k; rep(i,1,n){ cin>>a[i]; row[a[i]].pb(i); } rep(i,1,n){ cin>>b[i]; col[b[i]].pb(i+n); } rep(i,1,m){ int u,v; cin>>u>>v; vis[u][v]=true; } ll ans=0; rep(i,1,k){ if((int)row[i].size()==0 && (int)col[i].size()==0) continue; rep(j,1,2*n) edge[j].clear(); for(auto w:row[i]){ for(auto ww:col[i]){ if(vis[w][ww-n]){ edge[w].pb(ww); edge[ww].pb(w); } } } ll res=0; me(match,0,sizeof(match)); rep(j,1,2*n){ me(st,false,sizeof(st)); if(find(j)) res++; } ans+=1ll*((int)row[i].size()+(int)col[i].size()-res/2)*i; } cout<<ans<<'\n'; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮