zoukankan      html  css  js  c++  java
  • CF1519F Chests and Keys

    CF1519F Chests and Keys

    CF1519F Chests and Keys

    前半部分和另一篇题解基本相同:

    首先用式子来表示题意,即花费最小代价上锁以下条件成立:设 (L_x) 为宝箱 (x) 上锁的集合,则对于任意的打开的宝箱集合 (S),都要满足:(sumlimits_{iin S}a_ilesumlimits_{jin (igcup_{iin S}L_i )}b_j)

    这个东西形如HALL定理,而定理说的是点的数量关系,那么可以把 (a_i,b_j) 都看成点的数量,也就是进行拆点,然后转化为二分图上的问题。将每个宝箱 (i) 拆成 (a_i) 个点作为二分图的左部点;将每个锁 (j) 拆成 (b_j) 个点作为右部点。若宝箱 (i) 上有锁 (j),则将 (i) 拆出的所有点向锁 (j) 拆出的所有点连边,得到一个二分图。条件就是让这个图的左部点存在完美匹配

    考虑 dp:设 (f_{i,j}) 表示处理到宝箱 (i) 拆成的点,每个锁拆成的右部点中还没被匹配的个数(用五进制压缩成 (j)),最少需要花费的代价。转移时枚举当前宝箱对应的每个点匹配了哪个锁的点,加上这些锁的代价。(f_{n,*}) 的最小值即为答案。

    这样复杂度可以做到 (O(n·5^{2n})),由于有 (continue) 跑不满已经可以快速通过了。但保险起见,我们可以进一步优化这个 dp。

    (f_{i,j,k}) 表示处理到宝箱 (i),右部点剩余状态 (j),宝箱 (i) 还有 (k) 个点未匹配。先枚举 (i,j,k),转移的时候不用直接枚举右部匹配点的集合,只要遍历每个锁,再枚举从这个锁拿了几个点来转移。虽然在转移的过程中会出现“重复付钱”,但最优解显然会避开这种情况,对答案没有影响。

    复杂度 (O(n·5^{n}·4·m·4)=O(16n^2·5^{n})),但在实际测试中没啥差别,二者都飞一样快

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 8, M = 15625;
    int n, m, a[N], b[N], c[N][N], f[N][M][5];
    int v[M][N], p[N] = {0, 1, 5, 25, 125, 625, 3125};
    void get (int x) {
        for (int i = m, t = x; i >= 1; --i) v[x][i] = t / p[i], t %= p[i];
    }
    void Min (int &x, const int y) { if (y < x) x = y; }
    signed main() {
        read (n), read (m);
        for (int i = 1; i <= n; ++i) read (a[i]);
        for (int i = 1; i <= m; ++i) read (b[i]);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) read (c[i][j]);
        int mx = 0;
        for (int i = 1; i <= m; ++i) mx += p[i] * b[i];
        for (int i = 0; i <= mx; ++i) get (i);
        memset (f, 0x3f, sizeof (f)); int inf = f[0][0][0];
        f[1][mx][a[1]] = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = mx; j >= 0; --j) {
                for (int k = a[i]; k >= 0; --k) {
                    if (f[i][j][k] >= inf) continue;
                    for (int o = 1; o <= m; ++o)
                        for (int u = 1; u <= v[j][o] && u <= k; ++u) {
                            Min (f[i][j - p[o] * u][k - u], f[i][j][k] + c[i][o]);
                        }
                }
                Min (f[i + 1][j][a[i + 1]], f[i][j][0]);
            }
        int res = inf;
        for (int i = 0; i <= mx; ++i) res = min (res, f[n + 1][i][0]);
        printf ("%d
    ", res == inf ? -1 : res);
    }
    
  • 相关阅读:
    Vue内敛模板
    vue自定义组件添加原生事件监听
    vue 组件开发 props 验证
    Vue中子组件数据跟着父组件改变和父组件数据跟着子组件改变的方法
    jQuery中outerWidth()方法
    CSS3-transition
    行内元素(例如)设置float之后才能用width调整宽度
    leetcode LRU Cache python
    opcache effect
    leetcode Same Tree python
  • 原文地址:https://www.cnblogs.com/whx666/p/CF1519F.html
Copyright © 2011-2022 走看看