洛谷 【动态规划4】树与图上的动态规划

题单链接

P1352 没有上司的舞会

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxN=6010;
int n,w[maxN],head[maxN],f[maxN][2],root,l,k,tot;
bool isroot[maxN];
struct Edge{
	int u,v,next;
}e[maxN]; 
void add(int u,int v){
	e[++tot].u=u;
	e[tot].v=v;
	e[tot].next=head[u];
	head[u]=tot;
}
void dfs(int x){
	f[x][1]=w[x];
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		dfs(y);
		f[x][0]+=max(f[y][1],f[y][0]);
		f[x][1]+=f[y][0];
	}
}
int main(){
	cin>>n;
	memset(isroot,true,sizeof isroot);
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=1;i<=n-1;i++){
		cin>>l>>k;
		isroot[l]=false;
		add(k,l);
	}
	for(int i=1;i<=n;i++){
		if(isroot[i]) root=i;
	}
	dfs(root);
	cout<<max(f[root][0],f[root][1])<<endl;
} 

P2015 二叉苹果树

  • 对于每个分支节点来说,有三种选择,(1)减去左子树(2)减去右子树(3)将节点个数合理分配给左右子树。需要在这三种中选取边权和最大的决策。
  • g [ x ] [ k ] g[x][k] g[x][k]表示以 x x x为根,含 k k k个节点的子树的最大边权和(包括 x x x通往父节点的边权)。
  • g [ x ] [ k ] = { 0 , k = = 0 x 通 往 父 节 点 的 边 权 , x 为 叶 节 点 x 通 往 父 节 点 的 边 权 + m a x ( g [ x 左 儿 子 ] [ i ] + g [ x 右 儿 子 ] [ k − 1 − i ] ) , x 为 非 叶 节 点 g[x][k]=\left\{ \begin{aligned} 0,k==0\\ x通往父节点的边权,x为叶节点\\ x通往父节点的边权+max(g[x左儿子][i]+g[x右儿子][k-1-i]),x为非叶节点 \end{aligned} \right. g[x][k]=0,k==0xxx+max(g[x][i]+g[x][k1i]),x
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=110;
int tot,n,m,q,head[maxn],lch[maxn],rch[maxn],fa[maxn],v[maxn],g[maxn][maxn];
struct Edge{
	int u,v,w,next;
}e[2*maxn];
void add(int u,int v,int w){
	e[++tot].u=u;
	e[tot].v=v;
	e[tot].w=w;
	e[tot].next=head[u];
	head[u]=tot;
}
void dfs(int x){
	for(int i=head[x];i;i=e[i].next){		
		int y=e[i].v;
		if(y!=fa[x]){
			if(!lch[x]) lch[x]=y;
			else rch[x]=y;
			fa[y]=x;
			v[y]=e[i].w;
			dfs(y);
		}		
	}
}
int dp(int x,int k){
	if(k==0) return 0;
	if(g[x][k]>=0) return g[x][k];
	if(lch[x]==0) return (g[x][k]=v[x]);
	for(int i=0;i<k;i++){
		g[x][k]=max(g[x][k],dp(lch[x],i)+dp(rch[x],k-1-i));
	} 
	g[x][k]+=v[x];
	return g[x][k];
}
int main(){
	cin>>n>>m;
	int x,y,z;
	for(int i=1;i<n;i++){		
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) g[i][j]=-1;
	}
	cout<<dp(1,m+1)<<endl; 
}

P2014 [CTSC1997]选课

  • 将0节点选入,构成了一棵以0节点为根的树。
  • f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i为根节点,选取 j j j个节点的最大得分 j > 0 j>0 j>0
  • f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i ] [ j − k ] + f [ v ] [ k ] ) , k ∈ [ 1 , j − 1 ] f[i][j]=max(f[i][j],f[i][j-k]+f[v][k]),k\in [1,j-1] f[i][j]=max(f[i][j],f[i][jk]+f[v][k]),k[1,j1]
  • 枚举时,由状态更新特点可知 j j j需要逆向枚举.
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=310;
int n,m,credit[maxn],head[maxn],f[maxn][maxn],tot,k;
struct Edge{
	int to,next;
}e[maxn];
void add(int u,int v){
	e[++tot].to=v;
	e[tot].next=head[u];
	head[u]=tot;
}
void dfs(int x){
	f[x][1]=credit[x];
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].to;
		dfs(y);
		for(int j=m;j>0;j--){
			for(int k=1;k<j;k++){
				f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
			}
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>k>>credit[i];
		add(k,i);
	}
	m++;//选了0节点
	dfs(0);
	cout<<f[0][m]<<endl;
} 

P1613 跑路

  • G [ i ] [ j ] [ k ] G[i][j][k] G[i][j][k]表示从 i 到 j i到j ij是否存在一条长度为 2 k 2^k 2k的路径
  • d i s [ i ] [ j ] dis[i][j] dis[i][j]代表从 i 到 j i到j ij所需要的最短时间
#include<bits/stdc++.h>
using namespace std;
int dis[60][60],n,m;bool G[60][60][65];
void init(){
	memset(G,false,sizeof G);
	for(int i=1;i<=60;i++)
		for(int j=1;j<=60;j++)
			dis[i][j]=1e9;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		dis[x][y]=1;
		G[x][y][0]=true;
	}
	for(int k=1;k<=64;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int l=1;l<=n;l++){
					if(G[i][l][k-1]&&G[l][j][k-1]){
						G[i][j][k]=true;
						dis[i][j]=1;
					}
				}
			}
		}
	}
} 
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
}
int main(){
	init();
	floyd();
	cout<<dis[1][n]<<endl; 
}

P1122 最大子树和

  • f [ i ] f[i] f[i]表示以 i i i为根且包含 i i i的最大子树和
  • f [ i ] + = m a x ( 0 , f [ v ] ) , v f[i]+=max(0,f[v]),v f[i]+=max(0,f[v]),v i i i的孩子
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=16010;
int n,head[maxn],dp[maxn],tot,w[maxn],f[maxn],ans;
struct Edge{
	int from,to,next;
}e[2*maxn];
void add(int x,int y){
	e[++tot].from=x;
	e[tot].to=y;
	e[tot].next=head[x];
	head[x]=tot;
}
void dfs(int x,int fa){
	f[x]=w[x];
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].to;
		if(y!=fa){
			dfs(y,x);
			f[x]+=max(0,f[y]);
		}
	}
	ans=max(ans,f[x]);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>w[i];
	int x,y;
	for(int i=1;i<n;i++){
		cin>>x>>y;
		add(x,y);
		add(y,x);
	} 
	dfs(1,0);
	cout<<ans<<endl;
} 

P2585 [ZJOI2006]三色二叉树

  • f [ i ] [ 0 / 1 / 2 ] f[i][0/1/2] f[i][0/1/2]表示以 i i i为根的子树第 i i i个节点分别涂绿、红、蓝时绿色最多的节点。
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int N=1e6;
int n,ch[N][2],f[N][3],g[N][3],tot;
string s;
int build(){
	int now=++tot;
	if(s[now-1]=='2')ch[now][0]=build(),ch[now][1]=build();
	else if(s[now-1]=='1') ch[now][0]=build();
	return now;
}
void dfs(int x){
	int l=ch[x][0],r=ch[x][1];
	if(l) dfs(l);if(r) dfs(r);
	if(l==0&&r==0){
		f[x][0]=g[x][0]=1;
		f[x][1]=f[x][2]=g[x][1]=g[x][2]=0;
	}
	f[x][0]=max(f[l][1]+f[r][2],f[l][2]+f[r][1])+1;
	f[x][1]=max(f[l][0]+f[r][2],f[l][2]+f[r][0]);
	f[x][2]=max(f[l][0]+f[r][1],f[l][1]+f[r][0]);
	g[x][0]=min(g[l][1]+g[r][2],g[l][2]+g[r][1])+1;
	g[x][1]=min(g[l][0]+g[r][2],g[l][2]+g[r][0]);
	g[x][2]=min(g[l][0]+g[r][1],g[l][1]+g[r][0]);
}
int main(){
	cin>>s;n=s.size();
	dfs(build());
	cout<<max(f[1][0],max(f[1][1],f[1][2]))<<" "<<min(g[1][0],min(g[1][1],g[1][2]))<<endl;	
}

P1273 有线电视网

  • d p [ i ] [ j ] dp[i][j] dp[i][j]表示在以 i i i为根的子树中,满足 j j j个客户的需求所能获得的最大收益
  • 最终答案为 d p [ 1 ] [ i ] > = 0 dp[1][i]>=0 dp[1][i]>=0的最大的 i i i
  • d p [ i ] [ u ] [ j ] dp[i][u][j] dp[i][u][j]表示在以 u u u为根的子树中,仅考虑前 i i i个孩子,满足 j j j个客户端需求所能获得的最大收益, d p [ i ] [ u ] [ j ] = m a x ( d p [ i − 1 ] [ u ] [ j − k ] + d p [ v 的 孩 子 个 数 ] [ v ] [ k ] ) , v dp[i][u][j]=max(dp[i-1][u][j-k]+dp[v的孩子个数][v][k]),v dp[i][u][j]=max(dp[i1][u][jk]+dp[v][v][k]),v i i i的第 k k k个孩子。
  • 第一维进行优化, j j j需逆向枚举。因为 i i i是增大的,能够保证 d p [ v ] [ k ] = = d p [ v 的 孩 子 个 数 ] [ v ] [ k ] dp[v][k]==dp[v的孩子个数][v][k] dp[v][k]==dp[v][v][k]
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=3010;
int n,m,tot,dp[N][N],w[N],head[N];
struct Edge{
	int to,next,w;
}e[2*N];
void add(int u,int v,int w){
	e[++tot].to=v;
	e[tot].next=head[u];
	e[tot].w=w;
	head[u]=tot;
}
int dfs(int u){
	if(u>n-m){
		dp[u][1]=w[u];
		return 1;
	}
	int sum=0;
	for(int i=head[u];i;i=e[i].next){
		int y=e[i].to;
		int tk=dfs(y);
		sum+=tk;
		for(int j=sum;j>0;j--){
			for(int k=0;k<=min(j,tk);k++){
				dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[y][k]-e[i].w);
			}
		}		
	}
	return sum;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			dp[i][j]=-1e9;
		}
	}
	for(int i=1;i<=n-m;i++){
		int size,u,wi;cin>>size;
		for(int j=1;j<=size;j++){
			cin>>u>>wi;
			add(i,u,wi);
		}
	}
	for(int i=n-m+1;i<=n;i++){
		cin>>w[i];
	}
	for(int i=1;i<=n;i++){
		dp[i][0]=0;
	}
	dfs(1);
	for(int i=m;i>0;i--){
		if(dp[1][i]>=0){
			cout<<i<<endl;
			break;
		}
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值