• 【LuoguP3959】宝藏(NOIP2017)-状压DP:枚举子集


    测试地址:宝藏
    做法:本题需要用到状压DP。
    dp(i,j)为深度为i,已开发的屋子状态为j的最小花费,那么我们枚举和j不相交的集合k,可以这样转移状态:
    dp(i+1,j+k)=min(dp(i+1,j+k),dp(i,j)+dis(j,k)×(i+1))
    其中dis(j,k)指集合k内的每个点到集合j中与它相邻的点的最小距离的和。
    对于dis(j,k),我们可以先算出d(j,k):点k到集合j中相邻点的最小距离。这个显然可以O(n22n)求出。然后我们就可以算dis(j,k),由于kj不相交,所以枚举k实际上是枚举j的补集的子集,那么总的时间复杂度是O(n3n)的,可以接受。
    那么最上面的状态转移方程就可以计算了,也是枚举子集的形式,所以总的时间复杂度是O(n3n),可以通过此题。
    我傻逼的地方:没有特判n=1的情况(答案为0),WA了一个点……当然如果状态转移方程边界条件考虑得更全的话可能不用特判。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int inf=1000000000;
    int n,m,g[20][20],Dis[4210][4210],dis[4210][20],ans;
    int dp[20][4210];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        if (n==1) {printf("0");return 0;}
    
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                g[i][j]=inf;
        for(int i=1;i<=m;i++)
        {
            int a,b,v;
            scanf("%d%d%d",&a,&b,&v);
            g[a][b]=min(g[a][b],v);
            g[b][a]=g[a][b];
        }
    
        for(int i=0;i<(1<<n);i++)
            for(int j=1;j<=n;j++)
                if (!(i&(1<<(j-1))))
                {
                    dis[i][j]=inf;
                    for(int k=1;k<=n;k++)
                        if (i&(1<<(k-1))) dis[i][j]=min(dis[i][j],g[k][j]);
                }
        for(int i=0;i<(1<<n);i++)
        {
            int anti=(1<<n)-i-1;
            for(int j=anti;j;j=(j-1)&anti)
            {
                Dis[i][j]=0;
                for(int k=1;k<=n;k++)
                    if (j&(1<<(k-1)))
                    {
                        if (dis[i][k]==inf)
                        {
                            Dis[i][j]=inf;
                            break;
                        }
                        Dis[i][j]+=dis[i][k];
                    }
            }
        }
    
        for(int i=0;i<=n;i++)
            for(int j=0;j<(1<<n);j++)
                dp[i][j]=inf;
        for(int i=1;i<=n;i++)
            dp[0][1<<(i-1)]=0;
        ans=inf;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<(1<<n);j++)
                if (dp[i][j]<inf)
                {
                    int anti=(1<<n)-j-1;
                    for(int k=anti;k;k=(k-1)&anti)
                        if (Dis[j][k]<inf)
                            dp[i+1][j|k]=min(dp[i+1][j|k],dp[i][j]+Dis[j][k]*(i+1));
                }
            ans=min(ans,dp[i+1][(1<<n)-1]);
        }
        printf("%d",ans);
    
        return 0;
    }
  • 相关阅读:
    国外程序猿整理的机器学习资源大全
    一个改动配置文件的linux shell script
    python高精度浮点型计算的诡异错误
    错误:'dict' object is not callable
    AssertionError while merging cells with xlwt (Python)
    Python => ValueError: unsupported format character 'Y' (0x59)
    [转]Python的3种格式化字符串方法
    python requirements使用方法
    conda虚拟环境实践
    迭代器中next()的用法
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793400.html
Copyright © 2020-2023  润新知