洛谷P3959 宝藏(NOIp2017)

版权声明:蒟蒻Blog随意转载 https://blog.csdn.net/a1799342217/article/details/82632551

状压DP

题目传送门

为什么我会觉得prim是对的!为什么我70分的暴力都不打!果然我好菜

我是不会告诉你我不知道怎么枚子集的

n=12就是状压的范围。据说标算 O ( 4 n ) 我不会啊,优化过的 O ( n 3 n ) 我也不会啊,我只会 O ( n 2 3 n ) 不管了能过就行

Orz写模拟退火的

f [ i ] [ j ] [ s ] 表示以 i 为根的子树中, i 的深度的为 j ,子树中的节点集合为 s 的最小代价。那么有 f [ i ] [ j ] [ s ] = m i n { f [ k ] [ j + 1 ] [ s s ] + f [ i ] [ j ] [ s s s ] + a [ i ] [ k ] } ,其中 s s s 的子集, k s s 内的一个点。时间复杂度为 O ( n 3 3 n )

把转移方程中与 s 无关的提取出来,预处理 g [ i ] [ j ] [ s ] = m i n { f [ k ] [ j + 1 ] [ s ] + a [ i ] [ k ] } ,这样就是 O ( n 2 3 n ) 的了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 15
#define M (1<<12)+1
using namespace std;
int n,m,ans=1e9,a[N][N],f[N][N][M],g[N][N][M];
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++){
            a[i][j]=1e9;
            for (int s=1;s<1<<n;s++)
                if (s!=1<<i-1) f[i][j][s]=g[i][j][s]=1e9;
        }
    for (int i=1,x,y,z;i<=m;i++)
        scanf("%d%d%d",&x,&y,&z),a[x][y]=a[y][x]=min(a[x][y],z);
    for (int j=n-1;j;j--)
    for (int i=1;i<=n;i++)
    for (int s=1;s<1<<n;s++){
        for (int k=1;k<=n;k++)
            if (((1<<k-1)&s)&&a[i][k]!=1e9)
                g[i][j][s]=min(g[i][j][s],f[k][j+1][s]+a[i][k]*j);
        for (int ss=s&s-1;ss;ss=ss-1&s)
            f[i][j][s]=min(f[i][j][s],f[i][j][s^ss]+g[i][j][ss]);
    }
    for (int i=1;i<=n;i++) ans=min(ans,f[i][1][(1<<n)-1]);
    return printf("%d\n",ans),0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/82632551
今日推荐