树形dp

题目列表:P1122 P2016 P1352 P2015 P2014 P1273 P1272 P2458 P1131 P1270 P3177 

P1122最大子树和

#include<bits/stdc++.h>
using namespace std;
int n;
int v[16005];
vector<int> vec[16005];
int mx=~0x3f3f3f3f,root=0;
int dp[16005];

void dfs(int cur,int fa){
	dp[cur]=v[cur];
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		if(dp[to]<0)dp[to]=0;
		dp[cur]+=dp[to];
	}
}

int main(){
	scanf("%d",&n);
	
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
		if(mx<v[i]){
			root=i;mx=v[i];
		}
	}
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		vec[a].push_back(b);
		vec[b].push_back(a);
	}
	
	dfs(root,0);
	cout<<dp[root]<<endl;
	
	return 0;
} 

P2016战略游戏

#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> vec[1505];

int dp[1505][3];
void dfs(int cur,int fa){
	dp[cur][0]=1;
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		
		dp[cur][0]+=min(dp[to][0],dp[to][1]);//放置士兵的位置
		dp[cur][1]+=dp[to][0];//没放置士兵的位置
	}
}

int book[1505];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int a;
		scanf("%d",&a);
		int t;
		scanf("%d",&t);
		for(int j=1;j<=t;j++){
			int temp;
			scanf("%d",&temp);
			vec[a].push_back(temp);
			book[temp]=1;
		}
	}
	
	int root=find(book,book+n,0)-book;

	dfs(root,-1);

	int ans=min(dp[root][0],dp[root][1]);
	cout<<ans<<endl;
	return 0;
}

P1352没有上司的舞会 战略游戏升级版

#include<bits/stdc++.h>
using namespace std;
int n;
int v[6005];
vector<int> vec[6005];
int dp[6005][2];
int flag[6005];

void dfs(int cur,int fa){
	dp[cur][1]=v[cur];
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		dp[cur][0]=max(dp[cur][0],dp[cur][0]+max(dp[to][1],dp[to][0]));//该位置没有上司
		dp[cur][1]=max(dp[cur][1],dp[cur][1]+dp[to][0]);//该位置为上司
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
	}
	
	
	for(int i=1;i<=n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		if(i==n)break;
		vec[b].push_back(a);
		flag[a]=1;
	}
	
	int ans=0;
	for(int i=1;i<=n;i++){
		if(flag[i])continue;
		dfs(i,0);
		ans+=max(dp[i][0],dp[i][1]);
	}
	
	cout<<ans;
	
	return 0;
} 

P2015二叉苹果树 树上分组背包

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[105][105];
vector<int> vec[105];

int sz[105];
int dp[105][105];
void dfs(int cur,int fa){
	sz[cur]=1;
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=min(m,sz[cur]-1);j;j--){
			for(int k=0;k<=min(j-1,sz[to]-1);k++){
				dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k-1]+v[cur][to]);
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		vec[a].push_back(b);
		vec[b].push_back(a);
		v[a][b]=c,v[b][a]=c;
	}
	
	dfs(1,0);

	cout<<dp[1][m];
	
	return 0;
}

P2014选课 同二叉苹果树 都是分组背包

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[305];
vector<int> vec[305];

int sz[305];
int dp[305][305];
void dfs(int cur,int fa){
	sz[cur]=1;
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=min(m,sz[cur]-1);j;j--){
			for(int k=0;k<=min(j-1,sz[to]-1);k++){
				dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k-1]+v[to]);
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		vec[i].push_back(a);
		vec[a].push_back(i);
		v[i]=b;
	}
	
	dfs(0,0);

	cout<<dp[0][m];
	
	return 0;
}

P1273有线电视网 树上分组背包

#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[3005];
int mp[3005][3005];
int money[3005];
vector<int> vec[3005];
int dp[3005][3005];
int sz[3005];

void dfs(int cur,int fa){
	if(vec[cur].size()==0){
		sz[cur]=1;
		dp[cur][1]=money[cur];
		return ;
	}
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=sz[cur];j;j--){
			for(int k=0;k<=sz[to];k++){
				if(j<k)break;
				dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k]-mp[cur][to]);
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n-m;i++){
		int t;
		scanf("%d",&t);
		for(int j=1;j<=t;j++){
			int a,b;
			scanf("%d%d",&a,&b);
			vec[i].push_back(a);
			mp[i][a]=b;
			mp[a][i]=b;
		}
	}
	for(int i=1;i<=m;i++){
		scanf("%d",&money[i+n-m]);
	}
	
	memset(dp,~0x3f,sizeof(dp));
	for(int i=1;i<=n;i++){
		dp[i][0]=0;
	}
	
	dfs(1,0);
	
	for(int i=m;i;i--){
		if(dp[1][i]>=0){
			cout<<i<<endl;
			return 0;
		}
	}
	
	return 0;
} 

P1272重建道路 

#include<bits/stdc++.h>
using namespace std;
int n,p;
vector<int> vec[155];
int dp[155][155];
int sz[155];
void dfs(int cur,int fa){
	sz[cur]=1;
	dp[cur][1]=vec[cur].size();
	
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=sz[cur];j>=1;j--){
			for(int k=1;k<=sz[to];k++){
				if(j<=k)continue;
				dp[cur][j]=min(dp[cur][j],dp[cur][j-k]+dp[to][k]-1);
			}
		}
	}
}

int book[155];
int main(){
	scanf("%d%d",&n,&p);
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		vec[a].push_back(b);
		book[b]=1;
	}
	int root=find(book+1,book+1+n,0)-book;
	
	memset(dp,0x3f,sizeof(dp));
	
	dfs(root,0);
	
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		ans=min(ans,dp[i][p]+((i==root)?0:1));
	}
	cout<<ans<<endl;
	return 0;
}

P2458[SDOI2006]保安站岗 分类讨论 加维表示3种状态

因为不能忽略子节点对父节点的影响,所以需要三种状态

分别为1.在当前节点放置一个保安 2.在至少一个子节点放置保安 3.在父节点放置一个保安

dp[cur][0]=v[cur]+\sum min(dp[to][0],dp[to][1],dp[to][2])

dp[cur][1]=\sum min(dp[to][0],dp[to][1])至少存在一次dp[to][0],否则补上sig=dp[to][0]-dp[to][1]来满足至少一次dp[to][0]

dp[cur][2]=min(dp[to][0],dp[to][1])

#include<bits/stdc++.h>
using namespace std;
int n;
int v[1505];
vector<int> vec[1505];

int dp[1505][3];
void dfs(int cur,int fa){
	dp[cur][0]=v[cur];
	
	int flag=0,sig=0x3f3f3f3f;
	for(int i=0;i<vec[cur].size();i++){
		int to=vec[cur][i];
		if(to==fa)continue;
		
		dfs(to,fa);
		
		dp[cur][0]+=min(dp[to][0],min(dp[to][1],dp[to][2]));
		if(dp[to][0]<=dp[to][1]){
			flag=1;
			dp[cur][1]+=dp[to][0];
			dp[cur][2]+=dp[to][0];
		}
		else {
			sig=min(sig,dp[to][0]-dp[to][1]);
			dp[cur][1]+=dp[to][1];
			dp[cur][2]+=dp[to][1];
		}
	}
	
	dp[cur][1]+=(flag==0)?sig:0;
}

int book[1505];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		v[a]=b;
		int t;
		scanf("%d",&t);
		for(int j=1;j<=t;j++){
			int temp;
			scanf("%d",&temp);
			vec[a].push_back(temp);
			book[temp]=1;
		}
	}
	
	int root=find(book+1,book+1+n,0)-book;
	
	dfs(root,0);

	int ans;
	ans=min(dp[root][0],dp[root][1]);
	cout<<ans;
	return 0;
}

P1131[ZJOI2007]时态同步

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define ll long long
ll n;

ll u[MAXN],v[MAXN],w[MAXN];
ll first[MAXN],nxt[MAXN],cnt;
void add(ll a,ll b,ll c){
	++cnt;
	nxt[cnt]=first[a];first[a]=cnt;
	u[cnt]=a,v[cnt]=b,w[cnt]=c;
}

ll dp[MAXN];
ll ans=0;
void dfs(int cur,int fa){
	ll mx=0;
	for(int i=first[cur];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)continue;
		
		dfs(to,cur);
		
		mx=max(mx,w[i]+dp[to]);
	}
	
	dp[cur]+=mx;
	
	for(int i=first[cur];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)continue;
		
		ans+=mx-(w[i]+dp[to]);
	}
}

int main(){
	scanf("%lld",&n);
	ll root;
	scanf("%lld",&root);
	for(ll i=1;i<n;i++){
		ll a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	
	dfs(root,0);
	
	cout<<ans<<endl;
	
	return 0;
}

P1270“访问”美术馆

#include<bits/stdc++.h>
using namespace std;
#define MAXN 405

int u[MAXN],v[MAXN],w[MAXN];
int cnt,first[MAXN],nxt[MAXN];
void add(int a,int b,int c){
	++cnt;
	nxt[cnt]=first[a];first[a]=cnt;
	u[cnt]=a;v[cnt]=b;w[cnt]=c;
}

int dp[MAXN][2005];

int flag[MAXN];
pair<int,int> mp[MAXN];
int n;
bool vis[MAXN];
void pre(int cur,int fa){
	flag[fa]++;
	
	if(flag[fa]>2){
		return ;
	}
	vis[cur]=1;
	
	add(cur,fa,mp[cur].first*2);
	add(fa,cur,mp[cur].first*2);
	for(int i=0;i<=mp[cur].second;i++){
		dp[cur][i]=5*i;
	}
 
	if(mp[cur].second!=0)return ;
	
	for(int i=1;i<=n;i++){
		if(vis[i])continue;

		pre(i,cur);
	}
}

int sz[MAXN];
void dfs(int cur,int fa){
	sz[cur]=mp[cur].second;
	
	for(int i=first[cur];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=sz[cur];j>=0;j--){
			for(int k=1;k<=sz[to];k++){
				if(j<k)break;
				dp[cur][j]=min(dp[cur][j],dp[to][k]+dp[cur][j-k]+w[i]);
			}
		}
	}
}


int main(){
	/*
	freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    */
	int tme;
	scanf("%d",&tme);
	int a,b,m=0;
	
	while(~scanf("%d%d",&a,&b)){
		++n;
		mp[n]=pair<int,int>(a,b);
		m+=b;
	}

	memset(dp,0x3f,sizeof(dp));
	
	pre(1,0);

	dp[0][0]=0;
	dfs(0,-1);
	
	for(int i=m;i>=0;i--){
		if(dp[0][i]<tme){
			cout<<i<<endl;
			return 0;
		}
	}
	return 0;
} 

P3177[HAOI2015]树上染色

权值val不只是在当前根节点的子树内,而是对于整棵树来说的,相当于将一颗树以这条边分成两半

并且这个权值是动态的,状态不同权值也不相同,所以需要在dfs的过程中计算

#include<bits/stdc++.h>
using namespace std;
#define MAXN 4005
#define ll long long

int n,m;
int u[MAXN],v[MAXN],w[MAXN];
int cnt,first[MAXN],nxt[MAXN];
void add(int a,int b,int c){
	++cnt;
	nxt[cnt]=first[a];first[a]=cnt;
	u[cnt]=a,v[cnt]=b,w[cnt]=c;
}

int sz[MAXN];
ll dp[MAXN][MAXN];
void dfs(int cur,int fa){
	dp[cur][0]=dp[cur][1]=0;
	sz[cur]=1;
	
	for(int i=first[cur];i;i=nxt[i]){
		int to=v[i];
		if(to==fa)continue;
		
		dfs(to,cur);
		sz[cur]+=sz[to];
		
		for(int j=min(m,sz[cur]);j>=0;j--){
			for(int k=0;k<=sz[to];k++){
				if(j<k)break;
				if(dp[cur][j-k]==-1)continue;//此边的另外一部分 状态的最多黑点数比较少 可能无法达到j-k个 属于不合法状态 所以当前状态需要直接跳过
				
				int numl=k,numr=m-k;
				ll val=1ll*((n-numr-sz[to])*(sz[to]-numl)+numl*numr)*w[i];//因为是整棵树的价值,所以需要用n来减
				dp[cur][j]=max(dp[cur][j],dp[to][k]+dp[cur][j-k]+val);
			}
		}
	}
}

int book[MAXN];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);add(b,a,c);
		book[b]=1;
	}
	
	int root=find(book+1,book+n+1,0)-book;
	
	memset(dp,-1,sizeof(dp));
	
	dfs(root,0);
	
	cout<<dp[root][m]<<endl;
	
	return 0;
}

D - Serval and Rooted Tree

每个叶节点的值不确定,但是可以改变思路,从大小顺序入手。

dp[i]表示第i个结点的值为第dp[i]大。

若为max结点,dp[i]等于所有子结点中最小的dp[j],越靠前越大。

若为min结点,dp[i]等于所有子结点中dp[j]的和,最大为第dp[j]大。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define inf 0x3f3f3f3f

int flag[MAXN];
int sz[MAXN];
int dp[MAXN];
vector<int> pv[MAXN];
void dfs(int cur,int fa){
	if(pv[cur].size()==0){
		dp[cur]=1;
		sz[cur]=1;
		return ;
	}
	if(flag[cur])dp[cur]=inf;
	
	for(int i=0;i<pv[cur].size();i++){
		int to=pv[cur][i];
		if(to==fa)continue;
		dfs(to,cur);

		sz[cur]+=sz[to];
		if(flag[cur]){
			dp[cur]=min(dp[to],dp[cur]);
		}
		else {
			dp[cur]+=dp[to];
		}
	}
}

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&flag[i]);
	}
	
	for(int i=2;i<=n;i++){
		int t;
		scanf("%d",&t);
		pv[t].push_back(i);
	}
	
	dfs(1,0);
	cout<<sz[1]+1-dp[1]<<endl;		
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值