`
yzmduncan
  • 浏览: 326970 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论
阅读更多

    树形DP,即是在一颗树上进行DP,一般是有叶子节点状态推出根节点状态。结合几个简单例子分析。

例1. POJ2342/POJ3342

【题意】

公司有n个人,每个人有价值vi,有一天举办年会,每个人都可以参加,但有严格的等级制度,参加活动时,不能同时出现a和a的上司,问如何才能使总和最大。

【分析】

每个人只有去和不去两种状态,设DP[i][0]和DP[i][1]分别表示第i个人不参加和参加年会,获得的总的最大价值。

则状态转移方程为:

DP[i][1] += DP[j][0],

DP[i][0] += max{DP[j][0],DP[j][1]};其中j为i的孩子节点。

这样,从根节点r进行dfs,最后结果为max{DP[r][0],DP[r][1]}。

POJ3342与上面类似,多了一个问题问最后最大的结果是不是唯一的,判断唯一性:当某个节点i取不取一样,并且它的某个孩子节点取不取一样,那么结果才不唯一。


例2. HDU2196

【题意】

一棵树,询问一颗树上每个点离它最远的点是哪个?

【分析】

一棵树上离它最远的点必定在树的直径上(否则可以找到离它更远的点),到底是两端点的哪个点呢?

求树的直径:从任意一点BFS,得到最远点k,则k点必定是树的直径其中一端点,再从k点BFS,得到另一端点j,在求解过程中用两个数组记录其它点到这两点的距离,最后比较较大者为答案。


例3. POJ1463

【题意】


一颗树,n个节点,现在要在节点处设置尽量少的哨兵,使得每条边都被观察到。

【分析】

一个节点要么设立哨兵,要么不设立,若节点i不设立哨兵,则节点i的所有的孩子节点必须设立哨兵;否则,看若节点i的孩子设立哨兵的值是否

小于不设哨兵的值,取二者中的较小者。

DP[i][0] = sum{DP[j][1]},

DP[i][1] = sum{min(DP[j][1],DP[j][0])} + 1, j是i的孩子节点。



例4. POJ4045

【题意】

n个城市节点构成的一棵树,节点i和节点j的电量损耗为I*I*R*i到j的路径所含边数,现在要修建一个供电站,使得总的损耗量最小。求最小

损耗和可以修建供电站的节点。

【分析】

一种简单的想法就是枚举所有节点,然后bfs求和,复杂度为O(n^2)。

首先,选定任意一点作为根节点dfs,设两个数组num[i]和dp[i],分别表示以节点i为根的子树的节点个数 和 根到其子节点的距离(边数)之和。

num[i] = sum{num[j]}+1,dp[i] = sum{num[j]+dp[j]}.

从root开始第二次dfs,依次考虑每个点作为根节点,考虑将x(当前根节点)的孩子y作为根节点:

dp[y] = dp[y] + (dp[x]-dp[y]-num[y])+(num[x]-num[y]) = dp[x]-2*num[y] + num[x],记录最小值即可。

附POJ2342和POJ4045代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int Max = 6005;

struct Node
{
    int fa;
    int value;
    vector<int> child;
}node[Max];
int dp[Max][2];
bool vis[Max];

int max(int a, int b)
{
    return a>b?a:b;
}

void dfs(int now)
{
    vis[now] = true;
    int s = node[now].child.size();
    for(int i = 0; i < s; i++)
    {
        int child = node[now].child[i];
        if(!vis[child])
        {
            dfs(child);
            dp[now][1] += dp[child][0];
            dp[now][0] += max(dp[child][0],dp[child][1]);
        }
    }
}

int main()
{
    int i;
    int a,b;
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(node,0,sizeof(Node));
        memset(vis,0,sizeof(vis));
        for(i = 1; i <= n; i++)
        {
            scanf("%d",&dp[i][1]);
            dp[i][0] = 0;
        }
        while(scanf("%d %d",&a,&b)!=EOF)
        {
            if(a==0&&b==0)
                break;
            node[a].fa = b;
            node[b].child.push_back(a);
        }
        int root = 1;
        for(i = 1; i <= n; i++)
        {
            if(!node[i].fa)
            {
                root = i;
                break;
            }
        }
        dfs(root);
        printf("%d\n",max(dp[root][0],dp[root][1]));
    }
    return 0;
}
 #include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int Max = 51000;
typedef __int64 int64;
int n;
int I,R;

struct Edge
{
    int to;
    int next;
}e[Max*2];
int head[Max],numm;
bool vis[Max];
int64 dp[Max],num[Max];

void addEdge(int from, int to)
{
    e[numm].to = to;
    e[numm].next = head[from];
    head[from] = numm++;
}

void dfs1(int now)
{
    dp[now] = 0;
    num[now] = 1;
    vis[now] = true;
    for(int i = head[now]; i != -1; i = e[i].next)
    {
        int next = e[i].to;
        if(!vis[next])
        {
            dfs1(next);
            num[now] += num[next];
            dp[now] += dp[next]+num[next];
        }
    }
}

void dfs2(int now)
{
    vis[now] = true;
    for(int i = head[now]; i != -1; i = e[i].next)
    {
        int next = e[i].to;
        if(!vis[next])
        {
            dp[next] = dp[now] - 2*num[next] + n;
            dfs2(next);
        }
    }
}

int main()
{
    int i,j;
    int t;
    int x,y;
    scanf("%d",&t);
    while(t--)
    {
        numm = 0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        scanf("%d %d %d",&n,&I,&R);
        for(i = 1; i < n; i++)
        {
            scanf("%d %d",&x,&y);
            addEdge(x,y);
            addEdge(y,x);
        }
        dfs1(1);
        memset(vis,0,sizeof(vis));
        dfs2(1);
        int64 result = ((int64)1)<<61;
        vector<int> ans;
        ans.clear();
        for(i = 1; i <= n; i++)
        {
            if(dp[i]<result)
            {
                ans.clear();
                ans.push_back(i);
                result = dp[i];
            }
            else if(dp[i]==result)
                ans.push_back(i);
        }
        printf("%I64d\n",result*I*I*R);
        int r = ans.size();
        for(i = 0; i < r; i++)
        {
            printf("%d",ans[i]);
            printf("%c",i==r-1?'\n':' ');
        }
        printf("\n");
    }
    return 0;
}
 
分享到:
评论

相关推荐

    ThinkMap-实现Android端的简易思维导图。可以保存数据。编辑树形图。.zip

    核心实现分布如下:2017-07-01TreeModel:树形结构的存储,树形结构的遍历,添加、删除节点;NoteModel:节点关联的指向,和Parent的指向;TreeView :绘制树形结构,对树形结构位置的纠正,实现View层的添加,删除,...

    建议收藏算法基础课模板大全

    基础算法 —— 代码模板链接 常用代码模板1——基础算法 排序 二分 高精度 前缀和与差分 双指针算法 位运算 离散化 区间合并 数据结构 —— 代码模板链接 常用代码模板2——数据结构 ...树形DP 记忆化搜索 贪心

    AcWing算法基础课模板大全

    基础算法 —— 代码模板链接 常用代码模板1——基础算法 排序 二分 高精度 前缀和与差分 双指针算法 位运算 离散化 区间合并 数据结构 —— 代码模板链接 常用代码模板2——数据结构 ...树形DP 记忆化搜索 贪心

    动态规划的一些简单问题课件

    动态规划初步,包括引入,01背包,分组背包,完全背包,线性dp,树形dp,区间dp,环形dp

    牛客 – 王国(虚树+树的直径)

    题目分析:真的很巧,上周刚学的虚树,读完这个题的第一反应就是可以用虚树简化题目,其实完全可以用树形dp跑一遍dfs出来,可奈何我dp不好,就只能用虚树来做了 题目中有两个点可以单独考虑,首先是权值相同的点,这...

    算法基础课程:算法备赛学程

    算法基础课程 算法准备赛学程序目标:1.基础算法分级二分高精度垂直和与差分双指针算法位置运算离散化区间合并 ...背包问题线性DP区间DP计数类DP数位统计DP状态压缩DP树形DP记忆化搜索 6.贪心 7.时空复杂度分析

    leetcode中国-leetcode_algo:leetcode相关算法和模板使用python

    树形DP 记忆化搜索 贪心 时空复杂度分析 提高知识点 动态规划——从集合角度考虑DP问题 1.1 数字三角形模型 1.2 最长上升子序列模型 1.3 背包模型 1.4 状态机模型 1.5 状态压缩DP 1.6 区间DP 1.

    leetcode338-Leetcode:力码

    第 338 章力码 ...DP 数字 名称 等级 是/否 104 三角形/最小总数 中等的 是 两个指针 数字 名称 等级 是/否 344 反转字符串 简单的 283 移零 简单的 是 位操作 数字 名称 等级 是/否 136 单号 简单的

    Leetcode经典01背包-LeetCode-2020:LeetCode2020每一天,快乐每一天

    Leetcode经典01背包 LeetCode-2020 马上进入2020找实习冲刺阶段,我决定以天为单位,记录每天做的LeetCode习题,方便后期整理。 C++文档神器推荐:cppreference.chm 1 LeetCode刷题记录(每日...并查集是一种树形数据结

    IOI国家集训队论文集1999-2019

    + [树形DP](#树形dp) + [优化](#优化-1) * [计算几何](#计算几何) + [立体几何](#立体几何) + [计算几何思想](#计算几何思想) + [圆](#圆) + [半平面交](#半平面交) * [矩阵](#矩阵) + [矩阵](#矩阵-1) + ...

    leetcode530-Leetcode:Leetcodepython解决方案

    简单的 矩阵问题的扩展 难的 Breath First Search 广度优先搜索 问题 描述 锯齿形打印二叉树 周边区域(DFS 或 BFS) 二叉树右侧视图 课程安排 K 站内最便宜的航班 字梯II 删除无效括号 打印二叉树 最小高度树 01...

    SuperTextView-从未如此惊艳!一个超级的TextView.zip

    它能够大量的减少你的App中的布局复杂程度,减少视图树的绘制时间。炸裂的AdjusterAdjuster被设计用来在SuperTextView的绘制过程中插入一些操作。这具有非常重要的意义。比如,默认实现的DefaultAdjuster能够动态的...

Global site tag (gtag.js) - Google Analytics