2021牛客暑期多校训练营3 C.Minimum grid 二分图最大匹配

原题链接:https://ac.nowcoder.com/acm/contest/11254/C

题意

有一个n*n的矩阵,要求在m个位置填上个数,每个数的大小是[0,k],给定每行每列的最大值,问填数总和的最小值。

分析

如果直接填数,我们发现最多只需要填2*n个数,即将所有的最大值都填上去。但我们发现,如果第i行的最大值和第j列的最大值相等,那么将aij填上数,可以少填一个数。因此我们推广一下,如果有n行和m列最大值相等,那么我们进行一次最大匹配,得到的匹配数就是减少填的数字。那么我们可以枚举最大值,然后不断进行二分图最大匹配,每个数值i的贡献也就是 ( n + m − m a t c h ) ∗ i (n+m-match)*i n+mmatchi

这题特地说明了最大值个数不会超过500,因此直接用匈牙利去跑都是足够的。

Code

#include <bits/stdc++.h>
#define lowbit(i) i & -i
#define Debug(x) cout << x << endl
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const ll INF = 1e18;
const double eps = 1e-3;
const int N = 1e6 + 10;
const int M = 1e6 + 10;
const int MOD = 998244353;
int n, m, k, vis[2005], match[2005];
vector<int> g[N], ve[N];
int mp[2005][2005];
bool dfs(int x) {
    for (auto v : g[x]) {
        if (!vis[v]) {
            vis[v] = 1;
            if (!match[v] || dfs(match[v])) {
                match[v] = x;
                return true;
            }
        }
    }
    return false;
}
int work(vector<int> r, vector<int> c) {
    if (r.empty() || c.empty()) return 0;
    memset(vis, 0, sizeof vis);
    memset(match, 0, sizeof match);
    for (int i = 1; i <= n; i++) g[i].clear();
    for (int i = 0; i < r.size(); i++) {
        for (int j = 0; j < c.size(); j++) {
            int u = r[i], v = c[j];
            if (mp[u][v]) g[u].push_back(v);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (g[i].empty()) continue;
        memset(vis, 0, sizeof vis);
        if (dfs(i)) ans++;
    }
    return ans;
}
void solve() {
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        int b; cin >> b;
        ve[b].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        int c; cin >> c;
        ve[c].push_back(i+n);
    }
    for (int i = 1; i <= m; i++) {
        int x, y; cin >> x >> y;
        mp[x][y] = 1;
    }
    ll ans = 0;
    for (int i = k; ~i; i--) {
        if (ve[i].empty()) continue;
        vector<int> c, r;
        for (auto it : ve[i]) {
            //cout << i << " " << it << endl;
            if (it <= n) r.push_back(it);
            else c.push_back(it-n);
        }
        ans += 1ll*(r.size() + c.size() - work(r, c)) * i;
    }
    cout << ans << endl;
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值