• CodeForces 1070J Streets and Avenues in Berhattan 性质+动态规划


    题目大意:

    你有$k$个数,分为$26$种

    对于每个数,你可以选择选进$A$集合或者$B$集合或者不选

    要求$A$集合中必须有$n$个数,$B$集合中必须有$m$个数

    记第$i$种数在$A$集合中的个数为$a_i$,$B$中个数为$b_i$

    试最小代价$sum a_i * b_i$

    $k leqslant 200000$,$n, m leqslant 30000$

    首先,我们打表得出一个结论,代价一定只由一种数得出

    考虑证明:

    我们不妨设代价由$A$得出,且集合$S_A$和$S_B$中分别有$i$个$A$和$a - i$个$A$

    那么,如果我们尝试用$B$来替换$A$,不妨设从$B$中抽了$j$个$B$扔进$A$中,且一共有$b$个$B$

    那么贡献差为$i * (a - i) - ((i - j) * (a - i + j) + j * (b - j))(0 leq j leq min(i, b))$

    化简后,为$2j^2 - j(2i - a+ b)$

    这是一个开口向上的,以$j$为自变量的二次函数

    最大值一定在端点取到,也就是$j = 0$或者$j = i$或者$j = b$取到

    这三种情况对应着代价由$A$得出或者由$B$得出

    我们可以枚举在中间的种类是哪一个

    之后再枚举放在$A$中的数能取多少个

    相应地我们可以知道最多可以放在$B$中多少个

    可以通过背包来实现上述问题

    由于实现不优,复杂度为$O(26^2 * (n+ m) + 26 * k)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
        int wr[50], rw;
        #define pc(iw) putchar(iw)
        tpr inline void write(ra o, char c = '
    ') {
            if(!o) pc('0');
            if(o < 0) o = -o, pc('-');
            while(o) wr[++ rw] = o % 10, o /= 10;
            while(rw) pc(wr[rw --] + '0');
            pc(c);
        }
    }
    using namespace std;
    using namespace remoon;
    #define sid 30050
    #define kid 200050
    
    char s[kid];
    bool f[kid];
    int n, m, k, t, cnt[50];
    
    inline int judge() {
        f[0] = 1;
        memset(f, 0, sizeof(f));    
        rep(i, 0, 25) drep(j, k, cnt[i])
        f[j] |= f[j - cnt[i]];
        rep(i, n, k - m) 
        if(f[i]) { puts("0"); return 1; }
        return 0;
    }
    
    inline void solve() {
        int ans = 1e9;
        rep(i, 0, 25) {
            memset(f, 0, (n + m) << 2); 
            f[0] = 1;
            rep(j, 0, 25) if(i != j)
            drep(v, n + m, cnt[j]) f[v] |= f[v - cnt[j]];
        
            rep(j, 0, n + m) 
            if(f[j]) {
                int l = max(n - j, 0);
                int r = max(m - (k - cnt[i] - j), 0);
                if(l + r <= cnt[i]) ans = min(ans, l * r);
            }
        }
        write(ans);
    }
    
    int main() {
        t = read();
        while(t --) {
            n = read(); m = read(); k = read();
            scanf("%s", s + 1);
            int sn = strlen(s + 1);
            memset(cnt, 0, sizeof(cnt));
            rep(i, 1, sn) ++ cnt[s[i] - 'A'];
            if(judge()) continue;
            solve();
        }
        return 0;
    }
  • 相关阅读:
    Drop goldengate用户时报ORA-00604 ORA-20782 ORA-06512问题解决
    如何查看机器品牌型号
    ORA-28040:没有匹配的验证协议
    oracle 12C CDB下开启wallet
    mysql备份和恢复
    手工模拟vip切换
    半同步复制的安装部署
    linux下安装mysql5.7.11(二进制方式)
    postgrep创建存储过程例子
    Nginx 是前端工程师的好帮手
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9846640.html
Copyright © 2020-2023  润新知