树状DP入门例题

P1352 没有上司的舞会
/*
没有上司的舞会
P1352
https://www.luogu.org/problem/P1352
题意:有一场舞会,每一个人都有一个快乐值,但不能和上司同时出现在舞会中,问这个舞会的快乐值最大为多少
解法:
树状DP
*/
#include <bits/stdc++.h>
using namespace std;
#define maxn 100000
struct node{
	int u,v;
	int next;
}edge[maxn];
int head[maxn],cnt;
int dp[maxn][2];
int a[maxn];
void init(){
	memset(head,-1,sizeof head);
	cnt=0;
}
void add(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa){
	dp[u][0]=0;
	dp[u][1]=a[u];
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		if(v!=fa){
			dfs(v, u);
			dp[u][0]+=max(dp[v][0],dp[v][1]);
			dp[u][1]+=dp[v][0];
		}
	}
}
int main(){
	int n,u,v;
	cin>>n;
	init();
	for(int i=1;i<=n;i++) cin>>a[i];
	while(cin>>u>>v&&u+v){
		add(u,v);
		add(v,u);
	}
	dfs(1,-1);
	cout<<max(dp[1][0],dp[1][1])<<endl;
	return 0;
}

The more, The Better
/*
The more, The Better
HDU - 1561
https://cn.vjudge.net/problem/HDU-1561
题意:每一个城市都有一定的宝物,允许攻克m个城堡,有些城堡不能直接攻克,要攻克它先必须攻克指定的城堡,问最多可以得到多少宝物
解法:
树状DP+01背包
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 1000
int dp[maxn][maxn];
int f[maxn][maxn];
int as[maxn];
struct node{
	int u,v;
	int next;
}edge[maxn];
int head[maxn];
int cnt;
int n,m,a,b;
void init(){
	memset(head,-1,sizeof head);
	memset(dp,0,sizeof dp);
	memset(f,0,sizeof f);
	memset(as,0,sizeof as);

	cnt=0;
}
void add(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u){
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		dfs(v);
		for(int k=m;k>=0;k--){
			for(int j=0;j<=k;j++){
				f[u][k]=max(f[u][k],f[u][k-j]+dp[v][j]);
			}
		}
	}
	for(int i=1;i<=m+1;i++)
      dp[u][i]=f[u][i-1]+as[u];
}
int main(){
	while(cin>>n>>m&&n+m){
		init();
		for(int i=1;i<=n;i++){
			cin>>a>>b;
			as[i]=b;
			add(a,i);
		}
		dfs(0);
		printf("%d\n",dp[0][m+1]);
	}
	return 0;
}

Binary Apple Tree

解法一:

/*
Binary Apple Tree
URAL - 1018
https://cn.vjudge.net/problem/URAL-1018
题面:有一棵二叉苹果树,每个节点都有苹果,现在让你保留下来m条枝头,问最多可以剩余多少个苹果
解法:
树状DP + 01背包
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 1000
int f[maxn][maxn];
int as[maxn];
struct node{
	int u,v,w;
	int next;
}edge[maxn];
int head[maxn];
int cnt;
int n,m;
void init(){
	memset(head,-1,sizeof head);
	memset(f,0,sizeof f);
	cnt=0;
}
int max(int a,int b){
	if(a>b) return a;
	return b;
}
void add(int u,int v,int w){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa){
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		if(v!=fa){
			dfs(v,u);
			for(int k=m;k>=0;k--){
				for(int j=1;j<=k;j++){
          if(f[u][k]<f[u][k-j]+f[v][j-1]+edge[i].w)
          f[u][k]=f[u][k-j]+f[v][j-1]+edge[i].w;
				}
			}
		}
	}
}
int main(){
	int a,b,c;
	while(cin>>n>>m){
		init();
		for(int i=1;i<n;i++){
			cin>>a>>b>>c;
			add(a,b,c);
			add(b,a,c);
		}
		dfs(1,1);
		printf("%d\n",f[1][m]);
	}
	return 0;
}

解法二:

/*
Binary Apple Tree
URAL - 1018
https://cn.vjudge.net/problem/URAL-1018
题面:有一棵二叉苹果树,每个节点都有苹果,现在让你保留下来m条枝头,问最多可以剩余多少个苹果
解法:
dfs + 记忆化 + 枚举  
*/
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 1000
int dp[maxn][maxn];
int f[maxn][maxn];
int as[maxn];
struct node{
	int u,v,w;
	int next;
}edge[maxn];
int head[maxn];
int cnt;
int n,m;
void init(){
	memset(head,-1,sizeof head);
	memset(dp,0,sizeof dp);
	cnt=0;
}
int max(int a,int b){
	if(a>b) return a;
	return b;
}
void add(int u,int v,int w){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa){
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		if(v!=fa){
			as[v]=edge[i].w;
			dfs(v,u);
			for(int k=m;k>=0;k--){
				for(int j=0;j<=k;j++){
					f[u][k]=max(f[u][k],f[u][k-j]+dp[v][j]);
				}
			}
		}
	}
	for(int i=1;i<=m+1;i++)
      dp[u][i]=f[u][i-1]+as[u];
}
int main(){
	int a,b,c;
	while(cin>>n>>m){
		init();
		for(int i=1;i<n;i++){
			cin>>a>>b>>c;
			add(a,b,c);
			add(b,a,c);
		}
		dfs(1,1);
		printf("%d\n",dp[1][m+1]);
	}
	return 0;
}

Strategic game
/*
Strategic game
POJ - 1463
https://cn.vjudge.net/problem/POJ-1463
题面:最小点覆盖
解法: 树状DP
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
#define maxn 100000
struct node{
  int u,v;
  int next;
}edge[maxn];
int head[maxn];
int dp[maxn][2];
int cnt;
void init(){
	memset(head,-1,sizeof head);
	memset(dp,0,sizeof dp);
	cnt=0;
}
void add(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa){
  //cout<<u<<endl;
  dp[u][0]=0;
  dp[u][1]=1;
  for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].v;
    if(v!=fa){
      dfs(v,u);
      dp[u][0]+=dp[v][1];
      dp[u][1]+=min(dp[v][0],dp[v][1]);
    }
  }
}
int main(){
  int n,m,k,a;
  while(cin>>n){
    init();
    for(int i=0;i<n;i++){
      scanf("%d:(%d)", &m, &k);
      for(int i=0;i<k;i++){
        cin>>a;
        add(m,a);
        add(a,m);
      }
    }
    dfs(0,0);
    cout<<min(dp[0][0],dp[0][1])<<endl;
  }
  return 0;
}

Cell Phone Network

/*
Cell Phone Network
POJ - 3659
https://cn.vjudge.net/problem/POJ-3659
题面:最小支配集
解法: 树状DP
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
#define maxn 500000
#define INF 0x3f3f3f3f
struct node{
  int u,v;
  int next;
}edge[maxn];
int head[maxn];
int dp[maxn][3];
int cnt;
void init(){
	memset(head,-1,sizeof head);
	memset(dp,0,sizeof dp);
	cnt=0;
}
void add(int u,int v){
	edge[cnt].u=u;
	edge[cnt].v=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa){
  dp[u][0]=1;
  dp[u][1]=0;
  dp[u][2]=0;
  int sum=0,inc=INF;
  bool judge=false;
  for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].v;
    if(v!=fa){
      dfs(v,u);
      dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));
      if(dp[v][0]<=dp[v][1]){
        sum+=dp[v][0];
        judge=true;
      }else{
        sum+=dp[v][1];
        inc=min(inc,dp[v][0]-dp[v][1]);
      }
      if(dp[v][1]!=INF&&dp[u][2]!=INF) dp[u][2]+=dp[v][1];
      else dp[u][2]=INF;
      dp[u][1]+=min(dp[v][0],dp[v][1]);
    }
  }
  if(inc==INF&&!judge) dp[u][1]=INF;
  else {
    dp[u][1]=sum;
    if(!judge) dp[u][1]+=inc;
  }
}
int main(){
  int n,u,v;
  while(cin>>n){
    init();
    for(int i=1;i<n;i++){
      cin>>u>>v;
      add(u,v);
      add(v,u);
    }
    dfs(1,1);
    cout<<min(dp[1][0],min(dp[1][1],dp[1][2]+1))<<endl;
  }
  return 0;
}
发布了70 篇原创文章 · 获赞 22 · 访问量 6510

猜你喜欢

转载自blog.csdn.net/weixin_44410512/article/details/100009738