zoukankan      html  css  js  c++  java
  • [BZOJ4883][Lydsy1705月赛]棋盘上的守卫[最小基环树森林]

    题意

    有一大小为 (n*m) 的棋盘,要在一些位置放置一些守卫,每个守卫只能保护当前行列之一,同时在每个格子放置守卫有一个代价 (w) ,问要使得所有格子都能够被保护,需要最少多少的代价。

    (2leq n,mleq 10^5 ,n*mleq 10^5)

    分析

    • 将行列看成 (n+m) 个点。将每个格点放置守卫看成所在行列连了一条边,然后把每条边定向,如果被指向表示当前格点对当前 行/列 进行了保护。

    • 这样就会有 (n+m) 个点,(n+m) 条有向边,同时每条边最多有 1 的入度。形成了基环树森林。

    • 最小基环树森林可以通过 (Kruskal) 贪心求解,证明仍然可以考虑反证法。

    • 总时间复杂度为 (O(n))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=2e5 + 7;
    int n,m,edc;
    int par[N],c[N];
    struct edge{
    	int last,to,dis;
    	edge(){}edge(int last,int to,int dis):last(last),to(to),dis(dis){}
    	bool operator <(const edge &rhs)const{
    	  return dis<rhs.dis;	
    	}
    }e[N*2];
    int getpar(int a){return par[a]==a?a:par[a]=getpar(par[a]);}
    LL Kruskal(){
        sort(e+1,e+1+edc);int cnt=0;LL res=0;
        for(int i=0;i<N;i++) par[i]=i;
    	for(int i=1;i<=edc;i++){
    		int x=getpar(e[i].last),y=getpar(e[i].to);0
    		if(x==y&&!c[x]) c[x]=1,cnt++,res+=e[i].dis;
    		if(x!=y&&!(c[x]&&c[y])) par[x]=y,c[y]|=c[x],cnt++,res+=e[i].dis;
    		if(cnt==n+m) return res;
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	for(int j=1,x;j<=m;j++){
    		scanf("%d",&x);
    		e[++edc]=edge(i,j+n,x);
    	}
    	printf("%lld
    ",Kruskal());
    	return 0;
    }
    
  • 相关阅读:
    iOS优化内存方法推荐
    Swift和OC,是编译型语言、解释性语言、运行时语言
    redis常用命令
    redis 基本类型
    spring中事务配置
    redis 基本概览
    ThreadLocal 类说明
    spring 中 AOP 功能
    ps抠图简单方法
    nginx配置文件中location说明
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/9804109.html
Copyright © 2011-2022 走看看