zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793400.html
Copyright © 2011-2022 走看看