• Luogu P3959 宝藏


    图论+状压DP+贪心

    首先可以发现在选边的过程中得到的总是一棵树

    所以贪心地想,对于已选的点集,对于其能扩展到的节点肯定是选择消耗成本最少的一个

    因为n很小,我们考虑状压DP

    设$dp[i][mask]$表示以$i$号节点作为出发点即根节点,$mask$表示已选节点

    因为选择路的成本跟根节点到该边的出发点的距离有关,又因为这是一棵树

    那么设$f[i][mask][j]$表示在当前这颗树中,第$j$号节点的深度为多少,如果不在已选点集中则为-inf

    那么可以将在已选点集可以扩展的边都处理出来,进行转移

    采用刷表法

    注意在转移过程中看似复杂度为$O(n^{4}*2^{n})$

    但是在转移的过程中需要加入剪枝,可以很快的通过测试数据

    #include <bits/stdc++.h>
    #define inf (int)1e9
    using namespace std;
    const int MAXN=1000;
    int n,m,dp[14][MAXN*5];
    int f[14][MAXN*5][14];
    int mp[14][14],w;
    vector <int> e[14];
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=0;i<n;i++)
        {
            for (int j=0;j<n;j++)
              mp[i][j]=inf;
        }
        for (int i=1;i<=m;i++)
        {
            int u,v,l;
            scanf("%d%d%d",&u,&v,&l);
            u--;v--;
            mp[u][v]=min(mp[u][v],l);
            mp[v][u]=min(mp[v][u],l);//注意重边的情况
        }
        int full;
        full=(1<<n)-1;
        for (int i=0;i<n;i++)
        {
            for (int mask=0;mask<=full;mask++)
              dp[i][mask]=inf;
        }
        for (int i=0;i<n;i++)
        {
            dp[i][1<<i]=0;
            f[i][1<<i][i]=1;
        }
        for (int mask=0;mask<=full;mask++)
        {
            for (int i=0;i<n;i++)//枚举由那个节点作为开始节点
            {
                if (dp[i][mask]==inf)//剪枝
                  continue;
                for (int j=0;j<n;j++)//枚举点集中的点
                {
                    if (f[i][mask][j]!=0)//判断是否在点集中
                    {
                        for (int k=0;k<n;k++)//枚举能扩展的边
                        {
                            if (mp[j][k]==inf || f[i][mask][k]!=0)//剪枝
                              continue;
                            if (dp[i][mask|(1<<k)]>dp[i][mask]+mp[j][k]*f[i][mask][j])//进行转移
                            {
                                dp[i][mask|(1<<k)]=dp[i][mask]+mp[j][k]*f[i][mask][j];
                                for (int p=0;p<n;p++)
                                  f[i][mask|(1<<k)][p]=f[i][mask][p];
                                f[i][mask|(1<<k)][k]=f[i][mask][j]+1;//更新节点的深度
                            }
                        }
                    }
                }
            }
        }
        int ans=inf;
        for (int i=0;i<n;i++)
          ans=min(ans,dp[i][full]);//统计答案
        printf("%d
    ",ans);
    }
  • 相关阅读:
    for循环使用详解(c语言版)
    在Spring中轻松写日志
    项目可行性分析的困难
    控制台游戏引擎CGE——贪吃蛇
    python做数据分析pandas库介绍之DataFrame基本操作
    什么是 JWT -- JSON WEB TOKEN
    .net core 单体应用基于策略模式授权
    ABP VNext 初始化数据库时报错
    ABP VNext简介及使用代码生成器ABPHelper自动生成代码
    使用jenkins 在docker中发布.net core应用
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11298335.html
Copyright © 2020-2023  润新知