树上动态规划的归纳总结

个人理解

树上动态规划类比线性动态规划 线性动态规划:该状态由前面一个状态转移而来 树上动态规划:父结点状态由所有子节点状态转移而来

其实学会了线性动态规划,树上动态规划自然会的差不多了

归纳模式一

相邻取与不取问题:树上的相邻结点不能同时取,求取得的最大值
设f[u][1],f[u][0]分别表示u取与不取的时候能取得的最大值
易知:
f[u][1]=sum(f[v][0])+a[u] f[u][0]=sum(max(f[v][1],f[v][0]))

例题

相邻取,但不取同问题:树上的点有三种染色方式,相邻点不能选取同一颜色,求选取某颜色的最大值
易知:
f[u][0]=max(maxf[v][1],max(f[v][2])+1
f[u][1]=max(maxf[v][0],max(f[v][2])
f[u][2]=max(maxf[v][0],max(f[v][1])

例题

#include<iostream>
#include<algorithm>
using namespace std;

const int N=6e3+100;

struct node{
    
    
	int u,v,next;
}e[N*2];
int vex[N],f[N][2],k,a[N];

void add(int u,int v){
    
    
	k++;
	e[k].u=u;
	e[k].v=v;
	e[k].next=vex[u];
	vex[u]=k;
}

void dfs(int u,int fa){
    
    
	f[u][1]=a[u];
	for(int i=vex[u];i;i=e[i].next){
    
    
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);
		f[u][1]+=f[v][0];
		f[u][0]+=max(f[v][1],f[v][0]);
	}
}


int main() {
    
    
	int n,u,v;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<n;i++){
    
    
		cin>>u>>v;
		add(v,u);
		add(u,v);
	}
	dfs(1,0);
	cout<<max(f[1][1],f[1][0]);
	return 0;
}
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=5e5+100;

char s[N];
int len,tot=-1,tree[N][2],fmax[N][3],fmin[N][3];

void pre_dfs(int root) {
    
    
	tot++;
	if(s[root]=='0')return;
	if(s[root]=='1') {
    
    
		tree[root][0]=tot+1;
		pre_dfs(tot+1);
		return;
	}
	if(s[root]=='2') {
    
    
		tree[root][0]=tot+1;
		pre_dfs(tot+1);
		tree[root][1]=tot+1;
		pre_dfs(tot+1);
		return;
	}
}
void dfs(int u) {
    
    
	if(s[u]=='0') {
    
    
		fmax[u][0]=fmin[u][0]=1;
		fmax[u][1]=fmax[u][2]=fmin[u][1]=fmin[u][2]=0;
	}
	if(s[u]=='1') {
    
    
		dfs(tree[u][0]);
		fmax[u][0]=max(fmax[tree[u][0]][1],fmax[tree[u][0]][2])+1;
		fmax[u][1]=max(fmax[tree[u][0]][0],fmax[tree[u][0]][2]);
		fmax[u][2]=max(fmax[tree[u][0]][0],fmax[tree[u][0]][1]);
		fmin[u][0]=min(fmin[tree[u][0]][1],fmin[tree[u][0]][2])+1;
		fmin[u][1]=min(fmin[tree[u][0]][0],fmin[tree[u][0]][2]);
		fmin[u][2]=min(fmin[tree[u][0]][0],fmin[tree[u][0]][1]);
	}
	if(s[u]=='2') {
    
    
		dfs(tree[u][0]);
		dfs(tree[u][1]);
		fmax[u][0]=max(fmax[tree[u][0]][1]+fmax[tree[u][1]][2],fmax[tree[u][1]][1]+fmax[tree[u][0]][2])+1;
		fmax[u][1]=max(fmax[tree[u][0]][0]+fmax[tree[u][1]][2],fmax[tree[u][1]][0]+fmax[tree[u][0]][2]);
		fmax[u][2]=max(fmax[tree[u][0]][0]+fmax[tree[u][1]][1],fmax[tree[u][1]][0]+fmax[tree[u][0]][1]);
		fmin[u][0]=min(fmin[tree[u][0]][1]+fmin[tree[u][1]][2],fmin[tree[u][1]][1]+fmin[tree[u][0]][2])+1;
		fmin[u][1]=min(fmin[tree[u][0]][0]+fmin[tree[u][1]][2],fmin[tree[u][1]][0]+fmin[tree[u][0]][2]);
		fmin[u][2]=min(fmin[tree[u][0]][0]+fmin[tree[u][1]][1],fmin[tree[u][1]][0]+fmin[tree[u][0]][1]);
	}
}
int main() {
    
    
	cin>>s;
	len=strlen(s);
	pre_dfs(0);
	dfs(0);
	cout<<max(fmax[0][0],max(fmax[0][1],fmax[0][2]))<<" "<<min(fmin[0][0],min(fmin[0][1],fmin[0][2]));
	return 0;
}

归纳模式二(经典的树上背包)

线性的背包:f[j]=max(f[j],f[j-w[i]]+v[i])
树上的背包:f[u][j]=max(f[v][j],

归纳模式三

树上选最优点问题:从树上选择一个点,其所有点到这个点的距离和最大
很显然,循环取点是超时的,但是可以发现,相邻点距离和有一定的转移关系,我们刚好可以利用这一点来实现O(n)的状态转移
状态转移方程为:f[v]=f[u]+size[1]-size[v]-size[v]

例题

#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e6+100;

struct node{
    
    
	int u,v,next;
}e[N*2];
int vex[N],size[N],k,f[N],dep[N];

void add(int u,int v){
    
    
	k++;
	e[k].u=u;
	e[k].v=v;
	e[k].next=vex[u];
	vex[u]=k;
}
void pre_dfs(int u,int fa){
    
    
	size[u]=1;
	for(int i=vex[u];i;i=e[i].next){
    
    
		int v=e[i].v;
		if(v==fa)continue;
		dep[v]=dep[u]+1;
		pre_dfs(v,u);
		size[u]+=size[v];
	}
}
void dfs(int u,int fa){
    
    
	for(int i=vex[u];i;i=e[i].next){
    
    
		int v=e[i].v;
		if(v==fa)continue;
		f[v]=f[u]-size[v]+size[1]-size[v];
		dfs(v,u); 
	}
}
int main() {
    
    
	int n,u,v;
	cin>>n;
	for(int i=1;i<n;i++){
    
    
		cin>>u>>v;
		add(v,u);
		add(u,v);
	}
	pre_dfs(1,0);
	for(int i=1;i<=n;i++)f[1]+=dep[i];
	dfs(1,0);
	
	int ans=1; 
	for(int i=2;i<=n;i++)if(f[i]>f[ans])ans=i;
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43602607/article/details/114092359
今日推荐