树形dp小练

1.题目:选课

题解:

f[i][j]表示以i为根选j个点的最大值,设一个虚根0,然后在树上跑01背包dp

代码:

#include <cstdio>
#include <iostream>
#define N 5000
using namespace std;
int tot,nxt[N],point[N],v[N],n,m,root,f[N][305],a[N];
void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}
void treedp(int x)
{
	for (int i=point[x];i;i=nxt[i])
	{
	  treedp(v[i]);
	  for (int j=m;j>=1;j--)
	    for (int k=1;k<=j;k++)
	      f[x][j]=max(f[x][j],f[x][j-k]+f[v[i]][k]);
	} 
	if (x) for (int j=m;j>=1;j--) f[x][j]=f[x][j-1]+a[x];
}
int main()
{
	int i;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
	{
		int ff;
		scanf("%d%d",&ff,&a[i]);
	    addline(ff,i);
	}
	treedp(0);//f[i][j]表示以i为根的树选了j个点 
	printf("%d",f[0][m]);  
}

2.题目:联合权值

题解:

这个题其实可以不用树形dp,用一个暴力就好。

ans1是:每个点作为一个中转点,如果可以产生联合权值,那一定和一个点相连的两个点,我们可以设置maxx1,maxx2表示与一个点相连的最大值和第二大值,相乘一下把所有点遍历一遍取一个最大值就好了

ans2是:要求一个和嘛,可以采用结合律,与一个点相连的有很多点,你可以先求出与每个点相连的权值总和,然后在遍历每一条边的时候,乘上 总和减去一个点 作为这条边产生的权值

代码:

#include <cstdio>
#include <iostream>
#define Mod 10007
#define N 200005
#define LL long long
using namespace std;
int a[N],x[N],y[N],maxx1[N],maxx2[N];LL sum[N];
void doit(int a,int &b,int &c)
{
	if (c<a){b=c; c=a; return;}
	if (b<a) b=a;
}
int main()
{
	int n,i,ans=0;
	scanf("%d",&n);
	for (i=1;i<n;i++) scanf("%d%d",&x[i],&y[i]);
	for (i=1;i<=n;i++) scanf("%d",&a[i]);
	for (i=1;i<n;i++)
	{
		int xx=x[i],yy=y[i];
		sum[xx]+=a[yy]; sum[yy]+=a[xx];
	    doit(a[xx],maxx1[yy],maxx2[yy]);
	    doit(a[yy],maxx1[xx],maxx2[xx]);
	}
	for (i=1;i<=n;i++)
	  ans=max(ans,maxx1[i]*maxx2[i]);
	LL ans2=0;
	for (i=1;i<n;i++)
	{
		ans2=((LL)a[x[i]]*(LL)(sum[y[i]]-a[x[i]])%Mod+ans2)%Mod;
	    ans2=((LL)a[y[i]]*(LL)(sum[x[i]]-a[y[i]])%Mod+ans2)%Mod;
	}
	printf("%d %lld",ans,ans2);
}

3.三色二叉树

题解:

f[i][0/1/2]以i为根节点,选红/绿/蓝得到的最大绿色节点数,从底部往上转移即可 

代码:

#include <cstdio>
#include <iostream>
#define N 500005
//f[i][0/1/2]以i为根节点,选红/绿/蓝得到的最大绿色节点数 
using namespace std;
int sz=1,l[N],r[N],f[N][3],g[N][3];
void read(int now)
{
	char c=getchar();
	if (c=='0') return;
	++sz; l[now]=sz; read(l[now]);
	if (c=='2') 
	{
		sz++;
		r[now]=sz;
		read(r[now]);
	}
}
void dpmax(int i)
{
	if (!i) return; 
	dpmax(l[i]); dpmax(r[i]);
	f[i][0]=max(f[l[i]][1]+f[r[i]][2],f[l[i]][2]+f[r[i]][1]);
	f[i][1]=max(f[l[i]][0]+f[r[i]][2],f[l[i]][2]+f[r[i]][0])+1;
	f[i][2]=max(f[l[i]][1]+f[r[i]][0],f[l[i]][0]+f[r[i]][1]);
} 
void dpmin(int i)
{
	if (!i) return; 
	dpmin(l[i]); dpmin(r[i]);
	g[i][0]=min(g[l[i]][1]+g[r[i]][2],g[l[i]][2]+g[r[i]][1]);
	g[i][1]=min(g[l[i]][0]+g[r[i]][2],g[l[i]][2]+g[r[i]][0])+1;
	g[i][2]=min(g[l[i]][1]+g[r[i]][0],g[l[i]][0]+g[r[i]][1]);
} 
int main()
{
	read(1);
	dpmax(1);
	printf("%d ",max(f[1][1],max(f[1][0],f[1][2])));
	dpmin(1);
	printf("%d",min(g[1][1],min(g[1][0],g[1][2])));
}
4. 时态同步

题意:

给定一棵有根树,每次操作使一条边的权值+1,问最少操作多少次可以使每个叶子节点到根的距离相等

题解:

这个题先用贪心的思路,可以知道对于一个点来说,它的  叶子结点到它的距离  最后要变成的值都是   一开始  所有叶子结点到它  的最大值

同时对于一个点来说,如果他可以修改靠上的边,一定比修改靠下的边优

我们设f[v]表示以v为根的树到叶子结点的最大值为多少,f可以一遍dfs出来。计算其父亲u的f[u]与f[v]的差值,那么对于这个差值修改u与v的边是最优的,从底部往上递推就ok啦

看看数据范围啊亲你要用longlong

代码:

#include <cstdio>
#include <iostream>
#define N 1000010
#define LL long long
using namespace std;
int tot,nxt[N],point[N],v[N],c[N];
LL f[N],ans;
void addline(int x,int y,int cc)
{
	++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=cc;
	++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=cc;
}
void dfs(int now,int fa)//f[v]表示以v为根的树到叶子结点的最大值为多少 
{
	for (int i=point[now];i;i=nxt[i])
	  if (v[i]!=fa)
	  {
	  	dfs(v[i],now);
	  	f[now]=max(f[now],f[v[i]]+c[i]);
	  }
}
void treedp(int x,int fa)
{
	for (int i=point[x];i;i=nxt[i])
	  if (v[i]!=fa)
	  {
	  	treedp(v[i],x);
	  	ans+=f[x]-f[v[i]]-c[i];
	  }
}
int main()
{
	int n,s,i;
	scanf("%d%d",&n,&s);
	for (i=1;i<n;i++)
	{
		int x,y,cc;
		scanf("%d%d%d",&x,&y,&cc);
		addline(x,y,cc);
	}
	dfs(s,0);
	treedp(s,0);
	printf("%lld",ans);
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值