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\n",ans);
}

 

posted @ 2019-08-04 15:17  SevenDawns  阅读(138)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end