2017 CCPC秦皇岛 H.Prime Set(二分图匹配+分类讨论)

Posted kaka0010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017 CCPC秦皇岛 H.Prime Set(二分图匹配+分类讨论)相关的知识,希望对你有一定的参考价值。

原题链接:https://ac.nowcoder.com/acm/problem/14372

题意

简化一下题意,就是你可以从n个数中选择k对数,要求选择的ai,aj满足 a i + a j = p r i m e a_i+a_j=prime ai+aj=prime i ! = j i!=j i!=j
接着将选择的k对取∪,问最大的集合size是多少。

分析

先考虑怎么合成质数,一般情况下质数都是奇数,因此肯定是奇数+偶数合成,当然存在2的例外。先不考虑2,那么就变成二分图的形式,我们先求最大匹配。假设最大匹配为ans,如果ans>=k,那么答案直接输出 k ∗ 2 k*2 k2。接下来考虑未匹配的1,如果存在,我们尽量先进行(1,1)匹配,然后再考虑可匹配点个数,假设剩余可匹配点的个数为num,那么答案就是 a n s ∗ 2 + m i n ( k − a n s , n u m ) ans*2+min(k-ans,num) ans2+min(kans,num)

Code

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<ll, ll> PII;
typedef unsigned long long ull;
const ll inf = 1e18;
const int N = 2e6 + 10, M = 1e6 + 10;
const int mod = 1e9 + 7;
vector<int> g[N];
int match[3010], vis[3010];
bool find(int x) {
    for (auto v : g[x]) {
        if (!vis[v]) {
            vis[v] = 1;
            if (!match[v] || find(match[v])) {
                match[v] = x;
                return true;
            }
        }
    }
    return false;
}
int a[N], b[N], c[N];
int prime[N];
bool is_prime[N];
void get_prime(){
    int k = 0;
    memset(is_prime, true, sizeof is_prime);
    is_prime[0] = is_prime[1] = false;
    for(int i = 2 ; i < N;i++){
        if (is_prime[i]) prime[++k] = i;
        for(int j = 1; j <= k && i * prime[j] < N;j++){
            is_prime[i * prime[j]] = false;
            if(i % prime[j] == 0) break;
        }
    }
}
void solve() {
    get_prime();
    int T; cin >> T; while (T--) {
        int n, k; cin >> n >> k;
        for (int i = 1; i <= n; i++) g[i].clear(), match[i] = 0, b[i] = c[i] = 0;
        for (int i = 1; i <= n; i++) cin >> a[i];
        vector<int> odd, even;
        for (int i = 1; i <= n; i++) {
            if (a[i] % 2 == 0) even.push_back(i);
            if (a[i] % 2 == 1) odd.push_back(i);
        }
        int f = 0;
        for (auto u : even) {
            for (auto v : odd) {
                if (is_prime[a[u]+a[v]]) {
                    g[u].push_back(v);
                    if (a[v] == 1) f = 1;
                    c[u] = c[v] = 1;
                }
            }
        }
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if (a[i] % 2 == 1) continue;
            memset(vis, 0, sizeof vis);
            if (find(i)) ans++, b[i] = 1;
        }
        if (ans >= k) printf("%d\\n", k*2);
        else {
            int cnt = 0, num = 0;
            for (auto u : even) if (!b[u] && c[u]) num++;
            for (auto v : odd) {
                if (match[v]) b[v] = 1;
                if (!b[v] && c[v]) num++;
                if (!match[v] && a[v] == 1) cnt++;
            }
            if (!f) {
                if (cnt > 1) num += cnt % 2;
                cnt /= 2;
                if ((cnt + ans) * 2 >= n) printf("%d\\n", n);
                else if (cnt + ans >= k) printf("%d\\n", k*2);
                else {
                    int mx = k - ans - cnt;
                    printf("%d\\n", (cnt+ans)*2+min(mx, num));
                }
            } else {
                cnt /= 2;
                num -= cnt * 2;
                if ((cnt + ans) * 2 >= n) printf("%d\\n", n);
                else if (cnt + ans >= k) printf("%d\\n", k*2);
                else {
                    int mx = k - ans - cnt;
                    printf("%d\\n", (cnt+ans)*2+min(mx, num));
                }
            }

        }
    }
}

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);
#endif
    solve();
    return 0;
}

以上是关于2017 CCPC秦皇岛 H.Prime Set(二分图匹配+分类讨论)的主要内容,如果未能解决你的问题,请参考以下文章

[CCPC] 2017秦皇岛H Prime Set | 二分图最大匹配 [据说是个金牌题]

ZOJ-3988 2017CCPC-秦皇岛 Prime Set 二分图最大匹配 匈牙利

2017CCPC秦皇岛

[CCPC] 2017秦皇岛 NumbersI | Java BigInteger | 贪心

2017CCPC秦皇岛G ZOJ 3987Numbers(大数+贪心)

思维题2017 CCPC现场赛秦皇岛站 问题 M: Safest Buildings