zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    linux 学习笔记
    linux 子系统折腾记 (三)
    linux子系统折腾记 (二)
    windows linux 子系统折腾记
    会计学习笔记(非专业)
    linux 大冒险
    coreRT 和 Native 编译netcore AOT程序
    dotnet core如何编译exe
    win10的hyper-v共享文件夹
    packagereference 里面的资产是怎么回事?
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11298335.html
Copyright © 2011-2022 走看看