树形DP从入门到入门

树形DP简单得不能再简单的清理

大致模版:

void dp(int u,int fa)
{
	f[][]= ;f[][]= ;
	for(int i=head[u];~i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa)continue;//防“倒吸”
		dp(v,u);
		balabalabala~~~~~~~;
	}
}

T1传送门 :Lg longest || MZOJ Longest
难度★
T2传送门:Lg 战略游戏
难度★
T3:Lg P1352【没有上司的舞会】
难度★★

T1

求一颗树的直径

【T1】

法一:BFS、DFS来回两遍
从任意一点出发找最长A,再从A找最长;
和树形DP没有一丁点关系

#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
queue<int>q;
struct EDGE
{
    int u,v,w,nxt;
}edge[maxn<<1];
int head[maxn],dist[maxn];
int size=0;
bool used[maxn];
void add(int u,int v,int w)
{
    edge[size].u=u;
    edge[size].v=v;
    edge[size].w=w;
    edge[size].nxt=head[u];
    head[u]=size++;
}


int n;
int read()
{
    int x=0;char ch=getchar();
    while(ch>'9' || ch<'0')ch=getchar();
    while(ch<='9' && ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}

void init()
{
    freopen("longest10.in","r",stdin);
    freopen("longest10.out","w",stdout);
}

void readdata()
{
    memset(head,-1,sizeof(head));
    n=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w);
        add(v,u,w);
    }
}

void bfs()
{
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=edge[i].nxt)
        {
            int v=edge[i].v,w=edge[i].w;
            if(!used[v])
            {
                q.push(v);
                dist[v]=dist[u]+w;
                used[v]=1;
            }
        }
    }
}

void work()
{
    
    memset(used,0,sizeof(used));
    memset(dist,0,sizeof(dist));
    q.push(1);used[1]=true;
    bfs();
    int maxd=0;
    int flag=0;
    for(int i=1;i<=n;i++)
    if(dist[i]>maxd)
    {
        maxd=dist[i];
        flag=i;
    }
    memset(used,0,sizeof(used));
    memset(dist,0,sizeof(dist));
    q.push(flag);used[flag]=true;
    bfs();
    maxd=0;
    for(int i=1;i<=n;i++)
    maxd=max(maxd,dist[i]);
    printf("%d",maxd);
}

int main()
{
	init();
    readdata();
    work();
    return 0;
}

法二:树形DP

如何求树的直径?

  1. 状态设计:f[i][0]表示到i的最长路径。f[i][1]表示到i的第二长路径,那么易得 f[i][0]+ f[i][1]即为经过i的最长路径。

  2. 状态转移:如果在儿子中还有存在更优解(f[son1][0]>f[father][0] || f[son1][0]>f[father][1])更新father的状态。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1000+5;
int size=0,ans=0;
int head[maxn],f[maxn][2];
struct EDGE
{
    int u,v,w,nxt;
}edge[maxn<<1];
 
void add(int u,int v,int w)
{
    edge[size].u=u;
    edge[size].v=v;
    edge[size].w=w;
    edge[size].nxt=head[u];
    head[u]=size++;
}
 
int n;
int read()
{
    char ch=getchar();int x=0;
    while(ch>'9' || ch<'0')ch=getchar();
    while(ch<='9' && ch>='0')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
 
void init()
{
    freopen("Longest DP.in","r",stdin);
}
 
void readdata()
{
    memset(head,-1,sizeof(head));
    n=read();
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        u=read();v=read();w=read();
        add(u,v,w);
        add(v,u,w);
    }
}
 
void dp(int u,int fa)
{
    f[u][0]=0;f[u][1]=0;
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v=edge[i].v,w=edge[i].w;
        if(v==fa)continue;
        dp(v,u);
        int dis=f[v][0]+w;
        if(dis>f[u][0])//两种情况:1.当儿子的值比fa最大值还要大时,
        {
            f[u][1]=f[u][0];//
            f[u][0]=dis;//
        }
        else if(dis>f[u][1])//2.当儿子的值<f[fa][0] && >f[fa][1]
        {
            f[u][1]=dis;
        }
        ans=max(ans,f[u][0]+f[u][1]);//找最优解
    }   
}
 
void work()
{
    dp(1,0);
    printf("%d",ans);
}
 
int main()
{
    //init();
    readdata();
    work();
    return 0;
}
/**************************************************************
    Problem: 1264
    User: mzg1811
    Language: C++
    Result: 正确
    Time:0 ms
    Memory:1760 kb
****************************************************************/

T2

T2

覆盖一棵树的最小代价

如何将一颗树完全附带且代价最小?

  1. 状态设计:f[i][0]表示i这个点不放士兵所需的最小代价,f[i][1]表示 表示i这个点士兵所需的最小代价

  2. 状态转移:画图可知
    1.如果i不放士兵,那么他的sons必须放士兵
    2.如果i放士兵,那么他的儿子可放可不放,求他们的最小值即可

#include <bits/stdc++.h>
using namespace std;
const int maxn=1500+5;
int n;
int f[maxn][2];
int size=0;
int head[maxn];
struct EDGE
{
	int u,v,nxt;
}edge[maxn<<1];

void add(int u,int v)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].nxt=head[u];
	head[u]=size++;
}

void dp(int u,int fa)
{
	f[u][0]=0;f[u][1]=1;
	for(int i=head[u];~i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa)continue;
		dp(v,u);
		f[u][0]+=f[v][1];//必须放
		f[u][1]+=min(f[v][0],f[v][1]);//可放可不放
	}
}

void initdata()
{
	memset(head,-1,sizeof(head));
	memset(edge,0,sizeof(edge));
	size=0;
	memset(f,0,sizeof(f));
}

void init()
{
//	freopen("Defense MZOJ1063.in","r",stdin);
}

void readdata()
{
	while(scanf("%d",&n)!=EOF)
	{
		initdata();
		for(int i=1;i<=n;i++)
		{
			int u,t;
			scanf("%d%d",&u,&t);
			for(int i=1;i<=t;i++)
			{
				int v;
				scanf("%d",&v);
				add(u,v);
				add(v,u);
			}
			
		}
		dp(0,-1);//随便从那个点开始
		printf("%d\n",min(f[0][0],f[0][1]));//找最小
	}

}

void work()
{
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}

2019.1.19
11.01

更新一波

T3:Lg P1352【没有上司的舞会】

某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式:
第一行一个整数N。(1<=N<=6000)
接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)
接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。
最后一行输入0 0
输出格式:
输出最大的快乐指数。

1.状态设计:f[i][0/1]表示i这个点 0去||1不去 参加的最大值
2.状态转移:如果上司去了,那么下属一定不能去:f[fa][1]=Σf[儿子][0]+happy[i];如果上司没有去,下属可去可不去f[fa][1]=Σmax(f[儿子][0],f[儿子][1])

#include <bits/stdc++.h>
using namespace std;
const int maxn=6005;
int n,head[maxn],size=0,happy[maxn],f[maxn][2],ans=0;
struct EDGE
{
	int u,v,nxt;
}edge[maxn<<1];

void add(int u,int v)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].nxt=head[u];
	head[u]=size++;
}

int read()
{
	char ch=getchar();int x=0,flag=1;
	
	while(ch<'0' || ch>'9')
	{
		if(ch=='-') flag=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*flag;
}

void init()
{
	freopen("Lg 1352.in","r",stdin);
}

void readdata()
{
	memset(head,-1,sizeof(head));
	n=read();
	for(int i=1;i<=n;i++)
	{
		happy[i]=read();
	}
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v);add(v,u);
	}
}

void dp(int u,int fa)
{
	f[u][0]=0;f[u][1]=happy[u];
	for(int i=head[u];~i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(v==fa)continue;
		dp(v,u);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}

void work()
{
	dp(1,1);
	for(int i=1;i<=n;i++)
		for(int j=0;j<=1;j++)
			ans=max(ans,f[i][j]);
			printf("%d",ans);
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}

19.1.19
17.17

猜你喜欢

转载自blog.csdn.net/qq_25845753/article/details/86549396