zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x56状态压缩DP AcWing529 宝藏

    题目链接:https://www.acwing.com/problem/content/531/

    题目给出不超过12个点,和一些边,第一个点不用花费,其余的点都要根据深度和扩展的边长来确定花费,通过dp,将层数作为阶段,每个阶段用状态压缩记录12个点中已经走过的点,转移的过程是从j状态转移到k,这里两个状态的转移一定要合法,在深度i-1转移到i时,对每个扩展的点到j集合的距离之和乘上(i-1).

    这里有一点十分重要,从j转移到k如何保证一定是从深度i-1的点扩展的呢?

    证明:

    ①、假设全部是从深度为i-1的点转移的,那么由于i-1层的状态都是最优的,加上新扩展的点到集合的距离也是最短的,无疑转移的结果是最优的。

    ②、假设原状态是S,加入一些新的点之后,新状态是S1,扩展的过程中包括一些点,这些点是从小于i-1层的状态扩展过来的,那么一定存在一个集合,包括S1中除了由i-1层扩展过来的结点以外的所有结点,这个状态在前i-1层中一定是最优的,把这个集合记为S2,那么显然从S2扩展到S1就是①中的情况。最优结果不会遗漏。

    证毕。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    using namespace std;
    const int N = 15,M=1<<12;
    int f[N][M];
    int expand[M],road[M][N];
    vector<int> valid[M],cost[M];//每个状态的下一个合法状态以及状态扩展的最小代价和 
    int a[N][N]; 
    int n,m;
    void pre(){
        memset(road,0x3f,sizeof road);
        for(int k=0;k<1<<n;k++){
            expand[k]=k;
            for(int x=1;x<=n;x++){
                if(k>>(x-1) & 1){//集合中存在点x 
                    road[k][x]=0;
                    for(int y=1;y<=n;y++){//枚举x可达的点 
                        if(a[x][y]==0x3f3f3f3f)continue;
                        expand[k]|=1<<(y-1);//将这个点加到集合中去,注意这个点可能已经在k中 
                        road[k][y]=min(road[k][y],a[x][y]);//到集合k的最小距离 
                    }
                }
            } 
        }
        for(int j=0;j<1<<n;j++){
            for(int k=0;k<j;k++){//枚举j的子集,由于数量小,所以暴力枚举 
                if((k&j)==k && (expand[k]&j)==j){//k是j的子集并且j是expand[k]的子集 
                    valid[j].push_back(k);//(k,j)合法 
                    int sum=0;
                    for(int i=1;i<=n;i++){
                        if((j^k)>>(i-1) & 1)sum+=road[k][i];//差集的最短路之和 
                    } 
                    cost[j].push_back(sum);
                }
            }
        }
    }
    int main(){
        cin>>n>>m;
        memset(a,0x3f,sizeof(a));
        for(int i=1;i<=m;i++){
            int x,y,z;
            cin>>x>>y>>z;
            a[x][y]=a[y][x]=min(a[x][y],z);
        }
        pre();
        memset(f,0x3f,sizeof f);
        for(int i=1;i<=n;i++)
            f[1][1<<(i-1)]=0;//扩展第一层不需要资金
            
        int ans=f[1][(1<<n)-1];
        for(int i=2;i<=n;i++){
            for(int j=1;j<1<<n;j++){
                for(int p=0;p<valid[j].size();p++){
                    int k=valid[j][p];
                    f[i][j]=min(f[i][j],f[i-1][k]+(i-1)*cost[j][p]);
                }
            } 
            ans=min(ans,f[i][(1<<n)-1]);
        }
            
        cout<<ans<<endl;
    }
  • 相关阅读:
    [转]三维曲线投影到坐标轴
    [转]python matplotlib contour画等高线图
    [转]python (matplotlib)画三维图像
    excel挑选出每一行的最大值或最小值
    Origin 三种方法在图片中添加水平辅助线
    Origin画放大部分区域图片
    Origin画3DScatter图-第一节
    JFreechart绘制2D散点图
    Ctex入门指南笔记 -列表、表格、公式与图片
    Ctex安装与运行
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13414349.html
Copyright © 2011-2022 走看看