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;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

 

posted @ 2017-05-16 15:55  ljh_2000  阅读(841)  评论(0编辑  收藏  举报