BZOJ4883 [Lydsy2017年5月月赛]棋盘上的守卫

来源:互联网 发布:淘宝登录会员名怎么改 编辑:程序博客网 时间:2024/06/13 18:47

把行和列看成点,那么(x,y)这个各种就代表在第x行和第j列之间连一条权值为w(x,y)的边

每一行和每一列都要被一个守卫覆盖,那么就相当于对于每个点(每一行和每一列),我们要在他所相连的边中选择一条没有被选择过的边,并把这条边变成有向边,从自己出发,指向另一端。比如是从第x行指向第y列,那么就意味着在(x,y)防止一个行守卫,反之亦然

那么我们发现所有我们选出来的边一定组成一个环套树森林

那么就求最小生成环套树森林即可,用kruskal就行,证明不会,ljss和commoncBB了半个下午,最后似乎未果而终

#include<iostream>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<cstdlib>#include<cstdio>#include<map>#include<bitset>#include<set>#include<stack>#include<vector>#include<queue>using namespace std;#define MAXN 100010#define MAXM 1010#define ll long long#define eps 1e-8#define MOD 1000000007#define INF 1000000000struct edg{int x;int y;int v;edg(){}edg(int _x,int _y,int _v){x=_x;y=_y;v=_v;}friend bool operator <(const edg &x,const edg &y){return x.v<y.v;}};edg e[MAXN*2];int f[MAXN*2];int n,m;bool d[MAXN*2];int tot;ll ans;int fa(int x){return f[x]==x?x:f[x]=fa(f[x]);}int main(){int i,j,x;scanf("%d%d",&n,&m);for(i=1;i<=n;i++){for(j=1;j<=m;j++){scanf("%d",&x);e[++tot]=edg(i,n+j,x);}}sort(e+1,e+tot+1);for(i=1;i<=n+m;i++){f[i]=i;}for(i=1;i<=tot;i++){if(fa(e[i].x)!=fa(e[i].y)){if(d[fa(e[i].x)]&&d[fa(e[i].y)]){}else if(d[fa(e[i].x)]){f[fa(e[i].y)]=fa(e[i].x);ans+=e[i].v;}else{f[fa(e[i].x)]=fa(e[i].y);ans+=e[i].v;}}else{if(!d[fa(e[i].x)]){d[fa(e[i].x)]=1;ans+=e[i].v;}}}printf("%lld\n",ans);return 0;}/**/


原创粉丝点击