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);
    }
  • 相关阅读:
    使用Stream方式处理集合元素
    Consumer方法结合Lambda表达式的应用
    java-遍历字符串的两种方式:1.char charAt(int index);2.char[] toCharArray()
    java-成员变量与局部变量的测试
    java-统计字符串中各字符次数
    java-字符串的遍历和字符串数组的遍历
    java-String类的获取方法(indexOf();substring()等)
    java-模拟登陆练习
    java-String类中的各字符串判断(包括" "和null的区别)
    java-String类的常见面试题
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11298335.html
Copyright © 2011-2022 走看看