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


    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4883

    【题解】

    我们如果把行列建点,将一个实际的点看作一条边(连接对应行列),那么题目转化为

    对于边定向,使得每个点度数为1,权值和最小。

    稍微想一下会发现这样就相当于求一个n+m+1个边的最小生成带环森林。

    并查集记录下每个连通块是否带环,两个带环就说明不能合并。否则就贪心合并。类似kruskal

    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 2e5 + 10;
    const int mod = 1e9+7;
    
    # define RG register
    # define ST static
    
    int n, m, fa[M];
    bool hv[M]; 
    struct edge {
        int u, v, w;
        edge() {}
        edge(int u, int v, int w) : u(u), v(v), w(w) {}
        friend bool operator < (edge a, edge b) {
            return a.w < b.w;
        }
    }e[M];
    int en = 0;
    
    inline int getf(int x) {
        return x == fa[x] ? x : fa[x] = getf(fa[x]);
    }
    
    int main() {
        cin >> n >> m;
        for (int i=1, t; i<=n; ++i) {
            for (int j=1; j<=m; ++j) {
                scanf("%d", &t);
                e[++en] = edge(i, j+n, t);
            }
        }
        sort(e+1, e+en+1);
        for (int i=1; i<=n+m; ++i) fa[i] = i, hv[i] = 0;
        int cnt = 0;
        ll ans = 0;
        for (int i=1; i<=en; ++i) {
            int fu = getf(e[i].u), fv = getf(e[i].v);
            if(hv[fu] && hv[fv]) continue;
            if(fu != fv) {
                fa[fu] = fv;
                hv[fv] |= hv[fu];
                ans += e[i].w;
            } else {
                if(!hv[fu]) hv[fu] = 1, ans += e[i].w;
            }
        }
        cout << ans << endl;
        return 0;
    }
    View Code
  • 相关阅读:
    HQ-day17 CSS样式表基础①
    SQL 常用操作
    EXCEL 批量生成SQL
    js 顺序提交表单
    js 页面回调函数
    C# excel 常用操作
    C#关于LINQ
    JS 表单验证
    关于Cookie
    js 短信验证码功能
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj4833.html
Copyright © 2020-2023  润新知