CF95E Lucky Country 题解

Desciption

CF95E Lucky Country

Solution

非常巧妙的 \(dp\) 题,sto xy学长 orz

同一个区域的岛屿明显只能在一起,所以用并查集合并一下同一区域内的岛屿。

然后我们要找出用最少的边连出一个幸运地区。

那这不就是一个背包吗!

朴素的状态:\(dp_{i, j}\) 表示考虑到前 \(i\) 个区域,一共有 \(j\) 个岛屿的最小连边数。

朴素的转移方程:

\[dp_{i, j} = \min\{dp_{i - 1, j - a_i} + 1\} \]

\(a_i\):表示区域 \(i\) 中的岛屿个数。

显然会 T,考虑优化。

经典的二进制拆分就登场了,所以我们二进制拆一下跑个 01背包 即可。

Code

#include <bits/stdc++.h>

using namespace std;

namespace IO{
    inline int read(){
        int x = 0;
        char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
        return x;
    }

    template <typename T> inline void write(T x){
        if(x < 0) putchar('-'), x = -x;
        if(x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace IO;

const int N = 1e5 + 10;
int n, m, tot, cnt;
int f[N], siz[N];
int a[N], dp[N], w[N], v[N];

inline int find(int x){
    return f[x] == x ? x : f[x] = find(f[x]);
}

inline void prework(){
    for(int i = 1; i <= n; ++i){
        if(!a[i]) continue;
        int tmp = 1;
        while(a[i]){
            w[++cnt] = i * tmp, v[cnt] = tmp;
            a[i] -= tmp, tmp <<= 1;
            if(a[i] < tmp && a[i]){
                w[++cnt] = i * a[i], v[cnt] = a[i];
                break;
            }
        }
    }
}

inline bool check(int x){
    while(x){
        if(x % 10 != 4 && x % 10 != 7) return 0;
        x /= 10;
    }
    return 1;
}

int main(){
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) f[i] = i, siz[i] = 1;
    for(int i = 1; i <= m; ++i){
        int fu = find(read()), fv = find(read());
        if(fu != fv){
            if(siz[fu] < siz[fv]) swap(fu, fv);
            f[fu] = fv, siz[fv] += siz[fu];
        }
    }
    for(int i = 1; i <= n; ++i)
        if(find(i) == i) a[siz[i]]++;
    memset(f, 0x3f, sizeof(f));
    prework();
    f[0] = 0;
    for(int i = 1; i <= cnt; ++i)
        for(int j = n; j >= w[i]; --j)
            f[j] = min(f[j], f[j - w[i]] + v[i]);
    int ans = 1e9;
    for(int i = 1; i <= n; ++i)
        if(check(i)) ans = min(ans, f[i]);
    write(ans > n ? -1 : ans - 1);
    puts("");
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-12-27 23:12  xixike  阅读(35)  评论(0编辑  收藏  举报