BZOJ4883 [Lydsy2017年5月月赛]棋盘上的守卫
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:BZOJ4883
正解:贪心
解题报告:
又是用贪心模拟费用流…
考虑直接跑费用流,复杂度很不靠谱,那么用之前$get$的姿势:用贪心模拟费用流。
费用流建图的话非常$simple$,如果我们用类似最小生成树的方式,把边按边权从小到大往里面加。
我们观察一下费用流建的那张图,一次增广,本质上可以看成是选择了一个行编号和一个列编号,那么我们考虑每个点加入时的情况。相当于是每个行、列所代表的点的入度强制要为1,那么只有可能是带一个环的树;如果图是一棵树,那么必然可以匹配;如果是一棵n条边的基环树,那么也能匹配。分类讨论,除非两个都为图,否则就能匹配。按最小生成树的方式做就好了。
//It is made by ljh2000 //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 #include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <string> #include <queue> #include <cmath> #include <ctime> #define lc root<<1 #define rc root<<1|1 #define reg(i,x) for(int i=first[x];i;i=nxt[i]) using namespace std; typedef long long LL; const int MAXN = 200011; int n,m,cnt,fa[MAXN]; bool G[MAXN]; LL ans; struct edge{ int x,y,z; }e[MAXN]; inline bool cmp(edge q,edge qq){ return q.z<qq.z; } inline int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } inline void link(int x,int y,int z){ e[++cnt].x=x; e[cnt].y=y; e[cnt].z=z; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void work(){ n=getint(); m=getint(); int x,y,z; for(int i=n+m;i>=1;i--) fa[i]=i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { z=getint(); link(i,j+n,z); } sort(e+1,e+cnt+1,cmp); for(int i=1;i<=cnt;i++) { x=find(e[i].x); y=find(e[i].y); if(G[x] && G[y]) continue; if(x!=y){ fa[x]=y; G[y]|=G[x]; } else G[y]=1; ans+=e[i].z; } printf("%lld",ans); } int main() { #ifndef ONLINE_JUDGE freopen("4883.in","r",stdin); freopen("4883.out","w",stdout); #endif work(); return 0; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!