• 宝藏


    https://www.luogu.org/problem/P3959

    qwq首先,prim是错误的,可以被这组数据卡掉:数据

    然后,据说正解 O(4^n)O(4n) ,而我的做法是 O(n^33^n)O(n33n) ,渐进意义上小但是 n leq 12n12 的时候大。

    然后,我的常数小至 frac{1}{27}271 ,极限数据(就是12个点之间全有边)在sd的辣鸡WinXp上跑了0.8秒。luogu自测过了。

    我的想法是dp。

    状态:

    f_{i,j,S}(j otin S)fi,j,S(j̸S) ,表示从起点到节点j的距离为i,我现在要从j开始挖边,挖通集合S的最小代价。

    转移时,我枚举从节点j打出的第一条边,假设它是j->k,再枚举从k要打到的子集 S_2subset SS2S ,那么

    f_{i, j, S} = min_{k in S_2 subset S} (d[j][k] * (i + 1) + f_{i + 1, k, S_2 - {k}} + f_{i, j, S - S_2})fi,j,S=kS2Smin(d[j][k](i+1)+fi+1,k,S2{k}+fi,j,SS2)

    最后取所有 $f_{0,i,U-{i})$ 的最小值即可,其中 $U$ 是全集。

    时间复杂度。。。这个太难算,用了好多二项式系数恒等式,这里略去。

    程序中先枚举的 S_2S2 后枚举的 k in S_2kS2 ,并且预处理了每个集合的最小元素以加快枚举。

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    namespace rqy{
      const int N = 13;
      const int INF = 0x3f3f3f3f;
      int f[N][N][1 << N];
      int siz[1 << N], ttt[1 << N];
      int d[N][N];
      void main() {
        int n, m, x, y, z;
        scanf("%d%d", &n, &m);
        memset(f, 0x3f, sizeof f);
        memset(d, 0x3f, sizeof d);
        while (m--) {
          scanf("%d%d%d", &x, &y, &z);
          --x; --y;
          d[y][x] = d[x][y] = std::min(d[x][y], z);
        }
        int up = 1 << n;
        for (int i = 1; i < up; ++i) siz[i] = siz[i & (i - 1)] + 1;
        for (int i = 0; i < n; ++i) ttt[1 << i] = i;
        for (int i = 1; i < up; ++i) ttt[i] = ttt[i & -i];
        for (int i = 0; i < n; ++i)
          f[n - 1][i][0] = 0;
        for (int j = n - 2; ~j; --j)
          for (int i = 0; i < n; ++i) {
            f[j][i][0] = 0;
            for (int S = 1; S < up; ++S) if (((~S >> i) & 1) && (siz[S] <= n - j - 1))
              for (int S2 = S; S2; S2 = (S2 - 1) & S) if (f[j][i][S & ~S2] < f[j][i][S]) {
                int tmp = S2;
                for (int k = ttt[tmp]; tmp; k = ttt[tmp &= ~(1 << k)])
                  if (d[i][k] < INF)
                    f[j][i][S] = std::min(f[j][i][S],
                        (j + 1) * d[i][k] + f[j + 1][k][S2 & ~(1 << k)] + f[j][i][S & ~S2]);
              }
          }
        int ans = INF;
        for (int i = 0; i < n; ++i) ans = std::min(ans, f[0][i][(up - 1) & ~(1 << i)]);
        printf("%d
    ", ans);
      }
    };
    int main() {
      rqy::main();
      return 0;
    }
  • 相关阅读:
    C++实现高斯滤波器
    移动通信
    最简单的DLL
    C/C++ 编译和链接过程
    Servlet 详解
    对 Java 集合的巧妙利用
    Java 泛型
    Java 字符编码与解码
    HTTP 400 错误
    a 标签的四种样式
  • 原文地址:https://www.cnblogs.com/aprincess/p/11808359.html
Copyright © 2020-2023  润新知