1. 程式人生 > >BZOJ 4883 [Lydsy2017年5月月賽]棋盤上的守衛(最小生成環套樹森林)

BZOJ 4883 [Lydsy2017年5月月賽]棋盤上的守衛(最小生成環套樹森林)

print 我們 size -s nbsp long pan typedef 包含

【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4883

【題目大意】

  在一個n*m的棋盤上要放置若幹個守衛。
  對於n行來說,每行必須恰好放置一個橫向守衛;同理對於m列來說,
  每列必須恰好放置一個縱向守衛。每個位置放置守衛的代價是不一樣的,
  且每個位置最多只能放置一個守衛,一個守衛不能同時兼顧行列的防禦。
  請計算控制整個棋盤的最小代價。

【題解】

  我們將每行和每列看成一個點,用每個格子上的點的權值作為邊,
  這就構成了一個n+m個點的圖。
  我們發現對於n個點的集合,為了滿足題目中的要求,就至少要包含n條邊。


  所以題目就轉化成求圖中的最小生成環套樹森林。

【代碼】

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=200010;
LL ans;
int x,n,m,f[N],v[N];
struct E{int u,v,c;E(){}E(int u,int v,int c):u(u),v(v),c(c){};}e[N];
bool cmp(E x,E y){return x.c<y.c;}
int sf(int x){return x==f[x]?x:f[x]=sf(f[x]);}
int main(){
    while(~scanf("%d%d",&n,&m)){
        int cnt=ans=0;
        for(int i=1;i<=n+m;i++)f[i]=i,v[i]=0;
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
            scanf("%d",&x);
            e[++cnt]=E(i,j+n,x);
        }sort(e+1,e+cnt+1,cmp);
        for(int i=1;i<=cnt;i++){
            int fx=sf(f[e[i].u]),fy=sf(f[e[i].v]);
            if(v[fx]&&v[fy])continue;
            if(fx!=fy)v[fy]|=v[fx],f[fx]=fy;else v[fx]=1;
            ans+=e[i].c;
        }printf("%lld\n",ans);
    }return 0;
}

BZOJ 4883 [Lydsy2017年5月月賽]棋盤上的守衛(最小生成環套樹森林)