• 【NOIp2017】宝藏


    题面 

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

    题解

    考场上写的最暴力的状压$dp$,计算量是$2e8$级别的,但是$Van$的评测机非常没有信仰,发现只有$80pts$(只比真暴力多了$10$分)。。。。

    注意到层与层之间的转移是独立的。

    设$F[i][S0][S1]$为当前是第$i$层,$S0$是$1$到$i-1$层的节点集合,$S1$是第$i$层的节点集合。它的最小代价是多少。

    转移的时候枚举一个下一层的节点是啥,然后用这一层的和它连上去转移就行了。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ri register int
    #define S 540000
    #define B 4096
    #define N 12
    #define INF 1000000007
    using namespace std;
    
    inline int read() {
      int r=0,f=0; char c=getchar();
      while (c<'0' || c>'9') f|=(c=='-'),c=getchar();
      while (c>='0' && c<='9') r=(r<<1)+(r<<3)+(c^48),c=getchar();
      return f?-r:r;
    }
    
    int n,m;
    int dis[N][N],pp[N],num[B];
    int stk[S];
    int f[S],g[N][S];
    
    inline int trans(int s,int t) {
      return num[s]+num[t]*2;
    }
    
    inline void chkmin(int &a,int b) {
      if (b<a) a=b;
    }
    
    int main() {
      freopen("treasure.in","r",stdin);
      freopen("treasure.out","w",stdout);
      pp[0]=1;
      for (ri i=1;i<12;i++) pp[i]=pp[i-1]*3;
      num[0]=0;
      for (ri i=1;i<B;i++) {
        for (ri j=0;j<12;j++) if (i&(1<<j)) {
          num[i]=num[i-(1<<j)]+pp[j];
          break;
        }
      }
      n=read(); m=read();
      memset(dis,0x3f,sizeof(dis));
      for (ri i=1;i<=m;i++) {
        int u=read()-1,v=read()-1,w=read();
        if (dis[u][v]>w) dis[u][v]=dis[v][u]=w;
      }
      memset(f,0x3f,sizeof(f));
      memset(g,0x3f,sizeof(g));
      
      int U=(1<<n)-1;
      for (ri s=0;s<=U;s++) {
        int top=0;
        for (ri s0=s;s0;s0=(s0-1)&s) stk[++top]=s0;
        f[trans(s^U,0)]=0;
        while (top) {
          int s0=stk[top]; top--;
          for (ri i=0;i<n;i++) if (s0&(1<<i)) {
            int a=trans(s^U,s0);
            int b=trans(s^U,s0-(1<<i));
            for (ri j=0;j<n;j++) if ((s^U)&(1<<j) && dis[i][j]<INF) {
              f[a]=min(f[a],f[b]+dis[i][j]);
            }
            break;
          }
        }
      }
      for (ri j=0;j<n;j++) g[0][trans(0,1<<j)]=0;
      for (ri i=0;i<n-1;i++)
        for (ri s=0;s<=U;s++)
          for (ri s0=s;s0;s0=(s0-1)&s) {
            int tot=((s^U)|(s0))^U;
            for (ri s1=tot;s1;s1=(s1-1)&tot) if (f[trans(s0,s1)]<INF) {
              chkmin(g[i+1][trans((s^U)|s0,s1)],g[i][trans(s^U,s0)]+f[trans(s0,s1)]*(i+1));
            }
          }
      int ans=INF;
      for (ri i=1;i<n;i++)
        for (ri s=0;s<=U;s++) if (g[i][trans(s,U^s)]<ans) ans=g[i][trans(s,U^s)];
      cout<<ans<<endl;
    }

    然后就是一个神奇的优化,其实我们没必要记录当前层是什么,直接记录$S=S0|S1$即可,然后强制把它们连到第$i+1$层即可。

    正确性对我来说可以形象的理解一下。

    $mbox{sto aysn orz}$

    #include <cstdio>
    #define min(a,b) ((a)<(b)?(a):(b))
    
    const int maxn=12,inf=1000000000;
    int e[maxn][maxn],f[maxn][1<<maxn],tr[1<<maxn][maxn];
    
    int main(){
        freopen("treasure.in","r",stdin);
        freopen("treasure.out","w",stdout);
        int n,m,u,v,w,ans;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i){
            scanf("%d%d%d",&u,&v,&w);
            --u;--v;
            if(!e[u][v] || e[u][v]>w)e[u][v]=e[v][u]=w;
        }
        for(int i=0;i<(1<<n);++i)
            for(int j=0;j<n;++j)if(i&(1<<j))
                for(int k=0;k<n;++k)if(!(i&(1<<k)) && e[j][k])
                    if(!tr[i][k] || tr[i][k]>e[j][k])tr[i][k]=e[j][k];
        for(int i=0;i<n;++i)
            for(int j=0;j<(1<<n);++j)
                f[i][j]=inf;
        for(int i=0;i<n;++i)
            f[0][1<<i]=0;
        for(int i=1;i<n;++i)
            for(int j=0;j<(1<<n);++j)if(f[i-1][j]!=inf){
                int t=((1<<n)-1)^j;
                for(int k=t;k;k=(k-1)&t){
                    ans=0;
                    for(int a=0;a<n;++a)if(k&(1<<a)){
                        if(!tr[j][a]){ans=-1;break;}
                        else ans+=tr[j][a]*i;
                    }
                    if(ans>=0)f[i][j|k]=min(f[i][j|k],f[i-1][j]+ans);
                }
            }
        ans=inf;
        for(int i=0;i<n;++i)ans=min(ans,f[i][(1<<n)-1]);
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    投简历——个人记录
    光电经纬仪——查资料
    Spring Boot(十三):spring boot小技巧
    Spring Boot(十二):spring boot如何测试打包部署
    Python3 hasattr()、getattr()、setattr()函数简介
    Python3 格式化字符串
    Python3 join函数和os.path.join用法
    Python3 根据m3u8下载视频,批量下载ts文件并且合并
    it commit提示Your branch is up-to-date with 'origin/master'.
    git下,输入git log 进入log 怎么退出
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11830411.html
Copyright © 2020-2023  润新知