• loj2318 「NOIP2017」宝藏[状压DP]


    附带其他做法参考:随机化(模拟退火、爬山等等等)配合搜索剪枝食用。


    首先题意相当于在图上找一颗生成树并确定根,使得每个点与父亲的连边的权乘以各自深度的总和最小。即$sumlimits_{i}depth_i imes value_{i→fa}$。

    看数据范围想状压,固定好一个点为根,然后每个点选没选看做状态$0/1$压位,于是朴素思想是$f[S][S_0][d]$表示已经选了$S$,当前$d$层选了$S'$($S'subset S$),这样一定可以保证由$S'$导出第$d+1$层,更新答案,而$S$内部其他$x otin S'$则不影响更新答案。但是,这样做时间和空间双炸,原因在于$S'$的枚举耗费大量时间。真的有枚举的必要吗?如果把状态设计为$f[S][d]$可以否?如果这样,每次从与$S$联通的剩余点里选子集来更新$f[S|T][d+1]$,选的这些点与已选的连通块中的点的最短边可以预处理出来,如果这种最短边恰好连到了第$d$层,那就是合法的。那么直接$mincost(S,T)*(d+1)$代价更新即可。但是如果真正连到的是非第$d$层的话,这样答案会被多算。不过,这样算出的比实际答案劣,一定存在另一种方案,在之前就已经把这个点选上,从而得出更优解,也就是说这个劣解不会影响答案,最优解总是会被枚举出来并且来更新的,这么做相当于把劣解和最优解全部枚举一遍,$min$一定被最优解更新掉了。所以这个$S'$维度可省。只要枚举$S$,在枚举剩余点子集$T$并计算代价,更新即可。

    由于每个点都可以作为起始的根,于是$f[2^i][0]$都初始化为$0$。不过本人后来有点不清楚的是为什么不会撞到一起?比如不同根相同$S$和$d$的怎么处理?发现不影响,这样的话会取一个最优的,剩余点集显然与最优的相连才保证代价可能继续最优。具体的话感觉还是要感性理解。

    总结:简化维度时要利用性质,推出某些性质来简化,不是凭空去掉的

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #define dbg(x) cerr << #x << " = " << x <<endl
     7 using namespace std;
     8 typedef long long ll;
     9 typedef double db;
    10 typedef pair<int,int> pii;
    11 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    12 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    16 template<typename T>inline T read(T&x){
    17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    19 }
    20 const int INF=0x3f3f3f3f;
    21 int dis[1<<12][13],arr[1<<12];
    22 int f[1<<12][13];
    23 int a[13][13];
    24 int n,m,ans=INF;
    25 int s[13],t[13];
    26 inline void preprocess(){
    27     for(register int i=1;i<(1<<n)-1;++i){
    28         s[0]=t[0]=0;
    29         for(register int j=1;j<=n;++j)
    30             if(i&(1<<j-1))s[++s[0]]=j;
    31             else t[++t[0]]=j;
    32         for(register int j=1;j<=t[0];++j)
    33             for(register int k=1;k<=s[0];++k)
    34                 MIN(dis[i][t[j]],a[t[j]][s[k]]);
    35         for(register int j=1;j<=t[0];++j)if(dis[i][t[j]]<INF)arr[i]|=1<<t[j]-1;
    36     }
    37 }
    38 inline void dp(){
    39     for(register int i=1;i<=n;++i)f[1<<i-1][0]=0;
    40     for(register int d=0;d<n;++d)
    41         for(register int S=1;S<(1<<n)-1;++S)if(f[S][d]<INF)
    42             for(register int j=arr[S];j;j=(j-1)&arr[S]){
    43                 int res=0;
    44                 for(register int k=1;k<=n;++k)if((1<<k-1)&j)res+=dis[S][k];
    45                 MIN(f[S|j][d+1],f[S][d]+res*(d+1));
    46             }
    47 }
    48 
    49 int main(){//freopen("treasure.in","r",stdin);//freopen("treasure.out","w",stdout);
    50     read(n),read(m);memset(f,0x3f,sizeof f),memset(dis,0x3f,sizeof dis),memset(a,0x3f,sizeof a);
    51     for(register int i=1,x,y,z;i<=m;++i)read(x),read(y),read(z),MIN(a[x][y],z),a[y][x]=a[x][y];
    52     preprocess();
    53     dp();
    54     for(register int i=0;i<n;++i)MIN(ans,f[(1<<n)-1][i]);
    55     printf("%d
    ",ans);
    56     return 0;
    57 }
    View Code

    预处理后枚举$S$并枚举$T$,$T$可以近似看做补集,则这个过程可以看做子集枚举,每次在统计一下子集的$cost$,则复杂度$O(n3^n)$。但是好像被平方的吊打了。。。

  • 相关阅读:
    Scrapy中间件
    Scrapy简介
    Scrapy解析器xpath
    postman
    yarn
    brew 安装 yarn 时候失败
    immutability-helper 用途+使用方法
    js 正则
    react redux 应用链接
    react 事件传参数
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11538397.html
Copyright © 2020-2023  润新知