几个树形dp

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了几个树形dp相关的知识,希望对你有一定的参考价值。

1.重建道路

树形dp基础题,f[i][j]表示在i这个点我和我的子树联通块大小为j最少砍几条边。  

转移的时候,到下一个子树时上一个子树所有答案先++(此树直接砍掉不贡献答案),再继续dp。

注意更新答案时,如果不是跟答案还有+1(砍掉我和我父亲的),不然洛谷会挂四个点QAQ

马上就Noip了我还只能做这种水题QAQ 还挂

//Twenty
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
typedef long long LL;
using namespace std;
const int N=201;
int ans,dp[N][N],n,p;

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int ecnt,fir[N],nxt[N<<1],to[N<<1];
void add(int u,int v) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
	nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}

void dfs(int x,int fa) {
	dp[x][1]=0;
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
		int y=to[i];
		dfs(y,x);
		for(int j=p;j>=1;j--) {
			dp[x][j]++;
			for(int k=0;k<j;k++) 
				dp[x][j]=min(dp[x][j],dp[x][j-k]+dp[y][k]);
		}
	}
	if(x==1) ans=min(ans,dp[x][p]);
	else ans=min(ans,dp[x][p]+1);
} 

void work() {
	ans=1e9+7;
	memset(dp,127/3,sizeof(dp));
	dfs(1,0);
	printf("%d\n",ans);
}
 
void init() {
	read(n); read(p);
	for(int i=1;i<n;i++) {
		int x,y;
		read(x); read(y);
		add(x,y);
	}
}
 
int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    init();
    work();
    return 0;
}

 

2.P2014 选课 

树形dp水题。

dp[i][j]表示在i这个点,在我和我的儿子中选了j门课的最大学分。注意转移时若i!=0自己必须选,所以枚举我儿子选的课必须小于我和我儿子选的所有课,而i==0时则小于等于都ok。

//Twenty
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
typedef long long LL;
using namespace std;
const int maxn=307;
int n,m,dp[maxn][maxn],f[maxn],v[maxn];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int ecnt,fir[maxn],nxt[maxn],to[maxn],ans;
void add(int u,int v) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}

void dfs(int x) {
	dp[x][1]=v[x];
	for(int i=fir[x];i;i=nxt[i]) {
		int y=to[i];
		dfs(y);
		for(int j=m;j>=0;j--) 
			for(int k=0;k<=(j-(x!=0));k++) 
				dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]);
	}
	ans=max(ans,dp[x][m]);
}

void work() {
	dfs(0);
	printf("%d\n",ans);
}

void init() {
	read(n); 
	read(m);
	for(int i=1;i<=n;i++) {
		read(f[i]); 
		read(v[i]);
		add(f[i],i);
	}
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    init();
    work();
    return 0;
}

 

3.P2015 二叉苹果树

又是树形dp水题。。

跟上面两个没啥差,还是最简单的。没啥可说了。

好水呀。。

//Twenty
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
typedef long long LL;
using namespace std;
const int maxn=100;
int n,m,dp[maxn][maxn];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1],ans;
void add(int u,int v,int w) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
	nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
}

void dfs(int x,int fa) {
	for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
		int y=to[i];
		dfs(y,x);
		for(int j=m;j>=0;j--) 
			for(int k=0;k<j;k++) 
				dp[x][j]=max(dp[x][j],dp[x][j-k-1]+dp[y][k]+val[i]);
	}
}

void work() {
	dfs(1,0);
	printf("%d\n",dp[1][m]);
}

void init() {
	read(n); 
	read(m);
	for(int i=1;i<n;i++) {
		int x,y,z;
		read(x);
		read(y);
		read(z);
		add(x,y,z);
	}
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    init();
    work();
    return 0;
}

  

4.4753: [Jsoi2016]最佳团体

因为找题的时候已经知道是二分了,一开始有点懵逼,然后突然想起世界上有种叫01分数规划的东西,然后树形dp就ok了。

这个看起来是n^3的dp实际上似乎是n^2的,然后bzoj加了一组数据后先sz[x]+=sz[y]再转移就过不了了,然后改一下就卡过了。

//Twenty
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<ctime>
#define eps 1e-4
typedef long long LL;
using namespace std;
const int maxn=2507;
int n,m,b[maxn],a[maxn],f[maxn];
double l,r=10000,dp[maxn][maxn];

template<typename T> void read(T &x) {
    char ch=getchar(); T f=1; x=0;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int ecnt,fir[maxn],nxt[maxn],to[maxn],sz[maxn];
void add(int u,int v) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; 
}

void dfs(int x,double now) {
	if(x) dp[x][1]=(double)a[x]-(double)b[x]*now;
	if(x!=0) sz[x]=1; 
	else sz[x]=0;
	dp[x][0]=0;
	for(int i=fir[x];i;i=nxt[i]) {
		int y=to[i];
		dfs(y,now);
		for(int j=sz[x];j>=(x!=0);j--) {
			for(int k=0;k<=sz[y];k++) 
				dp[x][j+k]=max(dp[x][j+k],dp[x][j]+dp[y][k]);
		}
		sz[x]+=sz[y];	
	}
}

void work() {
	while(r-l>=eps) {
		double mid=(l+r)/2;
		memset(dp,0xc2,sizeof(dp)); 
		dfs(0,mid);
		if(dp[0][m]>=eps) l=mid;
		else r=mid;
	}
	printf("%.3lf\n",l);
}

void init() {
	read(m);
	read(n);
	for(int i=1;i<=n;i++) {
		read(b[i]);
		read(a[i]);
		read(f[i]);
		add(f[i],i);
	}
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    init();
    work();
    return 0;
}

  

 

以上是关于几个树形dp的主要内容,如果未能解决你的问题,请参考以下文章

树形dp

poj1655 树的重心 树形dp

JSOI Salesman 树形Dp

[P1273] 有线电视网 (树形DP+分组背包)

POJ - 1155 TELE 树形dp

树形DP小结