NOIP2017 宝藏 【状压dp】

1 篇文章 0 订阅

P3959


SOL

不错的一道简单树论状压dp

  • 一句话题解:状压枚举每一层节点计算贡献,dp出答案。(或者深搜)

  • 说具体一点,发现贡献和深度有关,加上巨小无比的数据范围,我们可以记录前一颗树的节点和深度,枚举下一层地节点,把树一层一层地填出来。

  • 只需要预处理出 : 把点集S1和S2连边的边权和最小值 \text{把点集S1和S2连边的边权和最小值} 把点集S1S2连边的边权和最小值 顺便判一下合法性。

  • 枚举子集,有一个结论: 2 n 2^n 2n个集合的子集个数和为 3 n 3^n 3n

  • 时间复杂度: O ( n 3 n ) O(n3^n) O(n3n)

- 据说深搜跑得贼快?


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define ri register int
#define in red()
#define gc getchar()
#define cs const
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num*f;
}
cs int lim=1<<12,inf=0x3f3f3f3f;
int f[20][lim],g[lim][lim],state,n,m,stk[lim<<1],top;
inline void ckmin(int &a,cs int &b){
	if(a>b)a=b;
}
#define lb(x)  (x&(-x))
signed main(){
//	freopen("data.in","r",stdin);
	n=in;m=in;
	state=(1<<n)-1;
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g));
	for(ri i=1;i<=m;++i){
		int x=in,y=in,z=in;
		--x;--y;
		ckmin(g[1<<x][1<<y],z);
		ckmin(g[1<<y][1<<x],z);
	}
	for(ri i=0;i<n;++i){
		for(ri j=0;j<=state;++j){
			 g[j][1<<i]=min(g[j^lb(j)][1<<i],g[lb(j)][1<<i]);
		}
	}
	for(ri i=1;i<=state;++i){
		int j=(~i)&state;
		top=-1;
		for(ri t=j;t;t=(t-1)&j){
			stk[++top]=t;
		}
		for(ri k=top;k>=0;--k){
			int t=stk[k];
			if(g[i][lb(t)]^inf&&g[i][t^lb(t)]^inf){
				g[i][t]=g[i][t^lb(t)]+g[i][lb(t)];
			}
		}
	}
	for(ri i=0;i<n;++i)f[0][1<<i]=0;
	for(ri i=1;i<n;++i){
		for(ri j=1;j<=state;++j){
			for(ri t=j;t;t=(t-1)&j){
				if(g[t][j^t]^inf&&f[i-1][t]^inf){
					ckmin(f[i][j],f[i-1][t]+i*g[t][j^t]);
				}
			}
		}
	}
	
	int ans=inf;
	for(ri i=0;i<n;++i)ans=min(ans,f[i][state]);
	cout<<ans<<'\n';


	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值