CCF CSP 201909-5 城市规划【树上背包】

题目链接

  • 定义 f [ x ] [ j ] f[x][j] f[x][j]表示以点 x x x为根的子树中选取 j j j个节点的最小路径和。
  • 最终答案就是 f [ r o o t ] [ k ] f[root][k] f[root][k]
  • 考虑当前节点与其直接子节点之间的关系:对答案的影响是每条边的贡献,贡献值为 子 树 中 选 的 节 点 个 数 ∗ 子 树 外 选 的 节 点 个 数 ∗ w 子树中选的节点个数*子树外选的节点个数*w w
    CCF CSP 201909-5 城市规划【树上背包】_第1张图片
  • x x x为根的子树依赖于内部更小的子树,每个子树都可以在其中选取有限个点,不同子树中选取的数量可以不同,类似于背包问题。本题需要用滚动数组。
  • 状态转移方程:设当前更新的边为 < x , y > <x,y>, y y y x x x的子节点。 f [ x ] [ j ] = m i n { f [ y ] [ l ] + f [ x ] [ j − l ] + l ∗ ( k − l ) ∗ w } f[x][j]=min\{f[y][l]+f[x][j-l]+l*(k-l)*w\} f[x][j]=min{f[y][l]+f[x][jl]+l(kl)w}.此方程中出现了 k k k,也就是说不能仅仅考虑这个子树中的所有情况,也就是说 f [ x ] [ j ] f[x][j] f[x][j]并不仅仅只表示了 x x x这个子树中的所有数值,而是从整体来考虑,对全局而言,选取的全部 k k k个点在子树中的全部贡献都已经计算在内。
#include
#define ll long long
using namespace std;
const int maxn=5e4+10;
//ct[i]用于存以i为节点的子树中重要节点的个数 
int  n,m,k,head[maxn],tot,ct[maxn];
ll f[maxn][110];
bool imp[maxn]; 
struct edge{
	int to,next,w;
}e[maxn*2];
void add(int x,int y,int w){
	e[++tot].to=y;
	e[tot].w=w;
	e[tot].next=head[x];
	head[x]=tot;
} 
void dfs(int x,int fa){
	f[x][0]=0;
	if(imp[x]){
		//点x是重要节点
		f[x][1]=0;
		ct[x]++; 
	}
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].to;
		if(y==fa) continue;
		dfs(y,x);
		ct[x]+=ct[y];
		for(int j=min(k,ct[x]);j>0;j--){
			for(int l=1;l<=j&&l<=min(k,ct[y]);l++){
				f[x][j]=min(f[x][j],f[y][l]+f[x][j-l]+l*(k-l)*e[i].w);
			}
		}
	}
}
int main(){
	memset(f,63,sizeof f);
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		int x;cin>>x;
		imp[x]=1;
	}
	for(int i=1;i<n;i++){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);
	}
	dfs(1,0);
	cout<<f[1][k]<<endl;
}

你可能感兴趣的:(CCF,CSP,认证,动态规划)