【题解】 P3959 宝藏

\(Description:\)

给出一张 \(n\)个点 \(m\) 条边的图,要求找出这张图的一个生成树,满足(每个点的深度乘上连出去所有的边的取值和)最小

\(Sample\) \(Input:\)

4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1

\(Sample\) $ Output:$

4

\(Hint:\)

\(1<=n<=12\) \(, 0<=m<=1000\)

\(Solution:\)

发现数据范围很小可以存下二进制,那大概就八九不离十是状压了。

考虑状压怎么做,发现好像状态很难确定。

但可以肯定一定有一维要存选了那些点。

那么考虑有什么东西会影响答案,发现有深度和出边边权和两个。

又发现其实出边边权是可以在状态预处理出来的并且记它也不现实。

那么说明还要记一个深度,按照深度转移。

预处理出一个状态的子集变换到他的最小话费 $f[s][subset] $

转移方程就很好写了~~

\(f[i][j]=min(f[i][j],f[i-1][k]+dis[k][j]*(i-1))\) 方便期间这里 \(i\) 表示的是层数。

注意小心全开 \(long\) \(long\) 时空双爆。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,Max_status;
const int N=15,M=(1<<12)+5;
const LL  INF=0x3f3f3f3f;
int a[N][N],dis[M][M];
LL  f[N][M];
LL  ans;
int main(){
    memset(f,0x3f,sizeof(f));
    memset(a,0x3f,sizeof(a));
    ans=INF;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        int u=0,v=0,w=0;
        scanf("%d%d%d",&u,&v,&w);
        a[u][v]=a[v][u]=min(a[u][v],w);
    }
    Max_status=(1<<n)-1;
    for(int i=0;i<=Max_status;++i){
        for(int j=(i-1)&i;j;j=(j-1)&i){
            int cset=i^j;
            for(int k=1;k<=n;++k) if(cset&(1<<(k-1))){
                int Min=INF;
                for(int l=1;l<=n;++l) if(j&(1<<(l-1)))
                    Min=min(Min,a[l][k]);
                if(Min!=INF) dis[j][i]+=Min;
                else { dis[j][i]=INF;break;}
            }
        }
    }
    for(int i=1;i<=Max_status;i<<=1) f[1][i]=0;
    for(int i=2;i<=n;++i) 
        for(int s=0;s<=Max_status;++s)
            for(int subset=(s-1)&s;subset;subset=(subset-1)&s)
                if(dis[subset][s]!=INF)
                    f[i][s]=min(f[i][s],f[i-1][subset]+(LL)dis[subset][s]*(i-1));
    for(int i=1;i<=n;++i) ans=min(ans,f[i][Max_status]);
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/JCNL666/p/10738786.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值