树形dp--树上涂色



P3691树上涂色
时间限制 : - MS   空间限制 : 65536 KB  
评测说明 : 时限1000ms
问题描述



  给出一棵有n个节点(编号1到n)的树,开始时每个节点都没有颜色。现在有A和B两种颜色,给第x号节点涂上A颜色需要花费Ax块钱,给x号节点涂上B颜色需要花费Bx块钱。
  如果i,j两个节点相邻,也就是i和j之间有边直接相连。若i号点已经涂成了A色,那么给j号点涂A色只需Aj/2块钱。若i号点已经涂成了B色,那么给j号点涂B色只需Bj/2块钱。也就是说,如果相邻点已经被涂了相同的颜色,当前点涂这种颜色的时候只需要一半的费用。
  现在需要将树上所有点都涂上颜色,问最少需要多少费用?
  如果i,j两个节点相邻,也就是i和j之间有边直接相连。若i号点已经涂成了A色,那么给j号点涂A色只需Aj/2块钱。若i号点已经涂成了B色,那么给j号点涂B色只需Bj/2块钱。也就是说,如果相邻点已经被涂了相同的颜色,当前点涂这种颜色的时候只需要一半的费用。
  现在需要将树上所有点都涂上颜色,问最少需要多少费用?



输入格式


第一行,一个整数n,表示节点总数
第二行,n个空格间隔的整数,分别表示给1到n号节点涂上A颜色所需费用
第三行,n个空格间隔的整数,分别表示给1到n号节点涂上B颜色所需费用
接下来n-1行,每行两个整数i和j,表示i和j节点间有边直接相连


输出格式


一行,一个整数,所需最小费用


样例输入 1


3
1 2 5
3 8 1
1 2
1 3


样例输出 1


3


样例输入 2


6
1 40 2 5 6 10
4 14 4 2 8 30
1 2
1 3
2 4
4 5
4 6


样例输出 2


25


提示


1<=n<=100

0<=Ax,Bx<=10000



分析:

状态:

f[i][0]:   以i为根的子树,涂上颜色的最小代价。其中i涂A颜色,与i连通的同色块(在子树内的)中,所有点都是以半价涂色的(即没有“涂色起点”)。
f[i][1]:  以i为根的子树,涂上颜色的最小代价。其中,i涂A颜色,与i连通的同色块中,存在一个节点是以全部代价涂色的(即有“涂色起点”)。
g[i][0]:  以i为根的子树,涂上颜色的最小代价。其中i涂B颜色,与i连通的同色块中,所有点都是以半价涂色的。
g[i][1]:  以i为根的子树,涂上颜色的最小代价。其中i涂B颜色,与i连通的同色块中,存在一个节点是以全部代价涂色


f[i][0]=Sum{ min( f[ j][0] , g[ j][1] ) }     ( j为i的所有儿子节点)

1.为什么不讨论f[j][1]呢?

2.为什么不讨论g[j][0]呢?

想一想,为什么?

方程:

f[i][0]=Sum{ min( f[ j][0] , g[ j][1] ) }     ( j为i的所有儿子节点)

f[i][1]=min{
           f[i][0]+ A[i]/2            //i涂全价A色
           f[i][0]+ ChaJia           //i的一个同色连通子树中存在一个节点涂全价A色
      }

 ChaJia=min{ f[j][1]-min(f[j][0] ,  g[j][1] )  }   j为i的所有儿子节点

说明:
f[i][1]表示i为根的字数中,i涂A色,且与i同色的连通块中,有一个点是全价A的最优方案。 我们可以对i涂全价A,也可以使与A同色连通的某棵子树中存在全价A的点。
而f[i][0]表示i为根的子树中,i涂A色,且与i同色的连通块全是半价A的最优方案。求f[i][1]只需在f[i][0]上做调整
如果我们直接将i涂成全价的A,即是f[i][0]+A[i]/2
如果我们不把i涂成全价的A,而是选一棵与i同色连通的且存在全价A的子树替换掉原来的子树,即是f[i][0]+ChaJia 这棵子树原来加入f[i][0]的状态是min(g[j][1],f[j][0]),而新替换进的状态是f[j][1],两者之差即为替换产生的差价。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define inf 1e9
using namespace std;
struct line{
	int from,to;
};
line edge[1000005];
int last[1005],_next[100005];
int n,m=0;
int a[105],b[105];
int f[105][5],g[105][5];
void add_edge(int x,int y){
	m++;
	_next[m]=last[x];
	last[x]=m;
	edge[m].from=x;
	edge[m].to=y;
}
void dp(int x,int fa){
	int i,j,k;
	bool leaf=true;
	int cha1=inf,cha2=inf;
	/*if(last[x]==0){
		f[x][1]=a[x];g[x][1]=b[x];
		f[x][0]=a[x]/2;g[x][0]=b[x]/2;
		return;
	}*/
	for(j=last[x];j;j=_next[j]){
		i=edge[j].to;
		if(i==fa)continue;
		dp(i,x);
		leaf=false;
		f[x][0]+=min(f[i][0],g[i][1]);
		g[x][0]+=min(f[i][1],g[i][0]);
		cha1=min(cha1,f[i][1]-min(f[i][0],g[i][1]));
		cha2=min(cha2,g[i][1]-min(f[i][1],g[i][0]));
	}
	if(leaf){
		f[x][1]=a[x];g[x][1]=b[x];
		f[x][0]=a[x]/2;g[x][0]=b[x]/2;
	}
	else{
		f[x][0]+=a[x]/2;
		g[x][0]+=b[x]/2;
		f[x][1]=f[x][0]+min(a[x]/2,cha1);
		g[x][1]=g[x][0]+min(b[x]/2,cha2);
	}
}
int main(){
	int i,j,k;
	cin>>n;
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(i=1;i<=n;i++){
		scanf("%d",&b[i]);
	}
	for(i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add_edge(x,y);
		add_edge(y,x);
	}
	dp(1,0);
	cout<<min(f[1][1],g[1][1]);
}
/*
	f[i][0]表示i涂a并且连通的全半价
	 f[i][1]表示i涂a并且连通的1个全价
	  g[i][0]表示i涂b并且连通的全半价
	 g[i][1]表示i涂b并且连通的1个全价
*/


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值