• LuoguP3959 宝藏 题解


    思路主要是抄_rqy的,这神仙位运算tql,整理一下思路。

    题目大意

    给定(n)个点,(m)条有权边,从一个点(s)开始挖(任选),形成一个生成树(即已经挖通的两个点间不能连边),挖一条边的代价为边的长度乘(s)到边的终点的距离,求最小代价。

    (n leq 12)

    思路

    状压dp。

    和一般的状压dp不一样,需要进行两层状压。

    (f[S][d][i])为挖开点集(S),现在距离起点的距离为(d),所在的点为(i)的最小代价。

    则$$f[S][d][i] = min_{S_1subset S}^{kin S1} w(i, k)(d+1) + f[S1-{k}][d+1][k] + f[S-S1][d][i]$$

    其中(i otin S, d+|S| leq n),因为当前至少已经挖开了(d)个点,最多只能挖开(n-d)个点。

    初始化:(f[0][d][i] = 0)

    注意(S)从小到大,(d)从大到小。

    最终结果就是$$min_{i=1}^nS[U-{i}][0][i]$$

    复杂度

    对于整个图,分成子集大小是(1)(n)的考虑。

    [T(n) = sum_{i=1}^n C_n^i 2^i = (2+1)^n = 3^n ]

    (二项式定理,大小为(i)的集合有(C_n^i)个,它的子集有(2^i)个)

    Code

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define File(IO_Filename) freopen(IO_Filename".in", "r", stdin), freopen(IO_Filename".out", "w", stdout)
    typedef long long ll;
    template<typename T> inline void in(T &x){
      char ch; x = 0;
      while(isspace(ch = getchar()));
      do x = x * 10 + ch - '0'; while(isdigit(ch = getchar()));
    }
    const int N = 13, INF = 0x3f3f3f3f;
    int w[N][N], pc[1<<N], low[1<<N]; //pc表示集合的元素个数,low表示集合内最小的元素(用来取子集)
    int f[1<<N][N][N];
    
    void wrbin(int x){
      if(x == 0) return ;
      wrbin(x >> 1);
      putchar('0' + (x & 1));
    }
    
    int main(){
      // File("3959");
      int n, m, x, y, wt, tot;
      in(n); in(m);
      memset(w, 0x3f, sizeof(w)); memset(f, 0x3f, sizeof(f));
      for(int i=1; i<=m; i++){
        in(x); in(y); in(wt); --x; --y;
        w[x][y] = w[y][x] = min(w[x][y], wt);
      }
      tot = 1 << n;
    
      for(int i=0; i<tot; i++) pc[i] = pc[i & (i-1)] + 1;
      for(int i=0; (1 << i) < tot; i++) low[1 << i] = i;
      for(int i=0; i<tot; i++) low[i] = low[i & (-i)];
      for(int d=n-2; d>=0; d--)
        for(int i=0; i<n; ++i)
          f[0][d][i] = 0;
    
      for(int d=n-2; d>=0; --d)
        for(int i=0; i<n; ++i){
          for(int S=1; S<tot; ++S) if(pc[S] <= n - d && (S & (1 << i)) == 0) //优化
            for(int S1=S; S1; S1=(S1-1)&S){ //取S的子集S1
              for(int t=S1, k=low[t]; t; t&=(t-1), k=low[t]){ //取S1的所有元素
                if(w[i][k] != INF && f[S1-(1<<k)][d+1][k] != INF && f[S-S1][d][i] != INF)
                  f[S][d][i] = min(f[S][d][i],
                           w[i][k] * (d+1) + f[S1-(1 << k)][d+1][k] + f[S-S1][d][i]);
              }
            }
        }
      int ans = INF;
      for(int i=0; i<n; i++)
        ans = min(ans, f[tot-1-(1<<i)][0][i]);
      printf("%d
    ", ans);
      return 0;
    }
    
  • 相关阅读:
    如何在百度文库里面免费下载东西
    CompareTo
    MySql常用日期函数(转载)
    Oracle之ORDER BY
    Spring之Ioc
    在使用与测绘有关软件中的困难
    HDOJ_1008_Elevator
    HDOJ_1005_Number Sequence
    HDOJ_1004_Let the Balloon Rise
    HDOJ_1003_MaxSum
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/solution-p3959.html
Copyright © 2020-2023  润新知