ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树形DP学习笔记

2019-08-15 21:01:06  阅读:205  来源: 互联网

标签:dfs int 笔记 树形 110 include 节点 DP mp


昨天b组第三题正解树形DP加tarjan求环;但我一个都不会。。。。。。

趁8.8放假,学习一波:

1.存树{

存树还是不太熟悉,邻接矩阵,邻接表,双亲,孩子;

我还是喜欢用邻接矩阵,好理解,再加双亲找根。

(后加)做了几题后发现用孩子表示法也挺方便的XD

}

2.dfs{

dp转移要么从子到父,要么从父到子,

在dfs时dp方程位置要注意

子到父应在dfs之后回溯时更新;

父到子应在dfs之前更新。

}

例题P1352 没有上司的舞会

分析:对于每一个节点,只有取和不取两种状态,根节点取了子节点一定不取,

但根节点不取子节点可取也可不取;

则根节点处最优值应该把它子节点最优值累加

方程如下:

其中i为根节点,j为子节点,0表示不取这个点,1表示取;

最后答案就只需要比较一下树顶取和不取取最大值就好啦;

代码如下

#include<iostream>
#include<cstdio>
using namespace std;
int parent[7000],happy[7000],n,f[7000][2];
bool child[6100][6100];
inline int read()
{
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return f*x;
}
int max(int x,int y){return x>y?x:y;}
void dfs(int x)
{
    f[x][1]=happy[x];f[x][0]=0;
    for(int i=1;i<=n;i++)
    {
        if(child[x][i])
        {
            dfs(i);
            f[x][1]+=f[i][0];
            f[x][0]+=max(f[i][0],f[i][1]);
        }
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)happy[i]=read();
    for(int i=1;i<=n;i++)
    {
        int a,b;
        a=read();b=read();
        if(!a&&!b)break;
        parent[a]=b;
        child[b][a]=1;
    }
    int geng=1;
    while(parent[geng])geng++;
    dfs(geng);
    int ans=max(f[geng][0],f[geng][1]);
    cout<<ans;
    return 0;
}
P2015 二叉苹果树

就像蓝书中说的一样,对于q根树枝,要选q+1个节点,根节点最优值转移方程如下图: 

j-k-1是因为要把根节点也算上,这样k+j-k-1+1=j;

f[i][j]表示的是在i号节点为根节点的树中取j个节点能取得的最优值;

那么题目中给的苹果在边上怎么办?

书中也给了解决办法,把边权下移到点中就行啦。

代码如下

#include<iostream>
#include<cstdio>
using namespace std;
int n,q,f[110][110],mp[110][110],a[110],l[110],r[110];
inline int read()
{
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return f*x;
}
void tree(int x)
{
    for(int i=1;i<=n;i++)
    if(mp[x][i]>=0)
    {
        a[i]=mp[x][i];
        mp[x][i]=-1;mp[i][x]=-1;
        l[x]=i;
        tree(i);
        break;
    }
    for(int i=1;i<=n;i++)
    if(mp[x][i]>=0)
    {
        a[i]=mp[x][i];
        mp[x][i]=-1;mp[i][x]=-1;
        r[x]=i;
        tree(i);
        break;
    }
}
int dp(int i,int j)
{
    if(j==0)return 0;
    if(l[i]==0&&r[i]==0)return a[i];
    if(f[i][j])return f[i][j];
    for(int k=0;k<=j-1;k++)
    f[i][j]=max(f[i][j],dp(l[i],k)+dp(r[i],j-k-1)+a[i]);
    return f[i][j];
}
int main()
{
    n=read();q=read();q++;
    int b,c,d;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    mp[i][j]=-1;
    for(int i=1;i<n;i++)
    {
        b=read();c=read();d=read();
        mp[b][c]=d;mp[c][b]=d;
    }
    tree(1);
    cout<<dp(1,q)<<endl;
    return 0;
}
View Code

P1040 加分二叉树

不明白哪里像树了,好像一个区间dp然后处理一下根找树就好了啊;

枚举根节点的时候为什么k从i开始枚举才对;

搞不懂,慢慢想,想出来再更新博客(逃)

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
const int N=50;
typedef long long ll;
ll n,f[N][N],gen[N][N];
void tree(int l,int r)
{
    if(l>r)return;
    cout<<gen[l][r]<<" ";
    if(l==r)return;
    tree(l,gen[l][r]-1);
    tree(gen[l][r]+1,r);
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&f[i][i]);
        gen[i][i]=i;
    }
    for(int l=1;l<n;l++)
    {
        for(int i=1;i+l<=n;i++)
        {
            int j=i+l;
            for(int k=i;k<=j;k++)
            {
                int maxx=f[i][k-1]*f[k+1][j]+f[k][k];
                if(f[i][k-1]==0)maxx=f[k+1][j]+f[k][k];
                if(f[k+1][j]==0)maxx=f[i][k-1]+f[k][k];
                if(maxx>f[i][j])
                {
                    f[i][j]=maxx;
                    gen[i][j]=k;
                }
            }
        }
    }
    cout<<f[1][n]<<endl;
    tree(1,n);
}
View Code

至此,8.8学习树形DP结束

牺牲放假时间学习;

昨晚七夕我还在码代码。

我。。。。。

 

标签:dfs,int,笔记,树形,110,include,节点,DP,mp
来源: https://www.cnblogs.com/Frost-Delay/p/11360504.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有