【SinGuLaRiTy-1015】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

对于所有的题目:Time Limit:1s  |  Memory:256 MB

第一题:最长链(HDU 2196 Computer)

题目描述

给定一棵有n个节点的树,求每个节点到其他节点的最大距离。

输入

输入第一行是一个自然数n(n≤10000);

接下来 (n−1) 行描述:第i行包含两个自然数 , 表示编号为i的节点连接到的节点编号和这条网线的长度..距离总长不会超过10^9. 每行中的两个数字用空格隔开。

输出

输出包含n行. 第i行表示对于离编号为i的节点最远的节点与该节点的距离Si(1≤i≤n)。

样例数据

样例输入 样例输出

3

1 1

1 2

2

3

3

数据规模

30% n≤100

100% n≤10000

题目分析

这道题是考试前一天课上讲的题目,如果我没记错的话应该是HDU 2196的原题。让我兴奋的是,尽管这道题没有被布置进作业当中,但我昨天就已经在HDU上AC了这道题,再打一遍代码也是相当的轻松,一种优越感油然而生......

下面,让我们来看看这道题的思路:对于任何一个节点M,我们可以将距离它最远的点的位置分为两类:①在以M为根节点的子树中,我们假设此种情况下的最远距离为maxdis_down;②在以M为根节点的子树之外,我们假设此种情况下的最远距离为maxdis_up。显而易见的是,此时这个点的最远距离ans[M]就为max(maxdis_down,maxdis_up)。我们重点看看第二种情况:maxdis_up=M到其父亲节点L的距离dis(M,L)+ans[L]。对此,我们可以定义一个dp[MAXN][3]的数组:

f[i][0]表示顶点为i的子树中,距离顶点i的最长距离(对应第一种情况);

f[i][1]表示i到其父亲节点的距离+其父亲的最长距离(对应第二种情况);

f[i][0]实际上不需要DP,dfs就可求解(本次dfs也求出次长距离);对于f[i][1],我们采取从父节点到子节点的策略。

假设有一L节点,它有n个子节点,我们用Vi来表示。

此时,我们又要分情况来讨论了:

①若Vi不是L的最长路径所经过节点,有f[Vi][1]=dis(Vi,L)+max(f[L][0],f[L][1]);

②若Vi是L的最长路径所经过节点,Vi的最长路径就不能再次被计算,我们就要“退而求其次”,使用第二长的路径Sec_dis,此时有f[Vi][1]=dis(Vi,L)+max(Sec_dis,f[L][1])。

<通过求树的直径解决>

树的直径是什么?就是在一棵树上存在的两点之间的最长距离所经过的路径。例如:

在该图中,树的直径,即距离最长的两点间路径为:7、5、2、1、3、6

在这里我们将用到一个结论:对于任意一个树中的节点来说,距离它最远的点一定是这棵树的直径的两个端点中的一个。【详细证明

通过这个结论,我们就可以先对两点进行dfs求最远点,确定树的直径的两个端点,然后对树中的点进行dfs求出长度即可。

STD Code

#include<cstring>
#include<cstdio>
#include<algorithm> #define MAXN 10010 using namespace std; struct node_a
{
int head;
}V[MAXN]; struct node_b
{
int v;
int w;
int next;
}E[MAXN]; int top; void init()
{
memset(V,-,sizeof(V));
top=;
}
void add_edge(int u,int v,int w)
{
E[top].v=v;
E[top].w=w;
E[top].next=V[u].head;
V[u].head=top++;
}
int dp[MAXN][];
void dfs1(int u)
{
int biggest=,bigger=;
for(int i=V[u].head;~i;i=E[i].next)
{
int v=E[i].v;
dfs1(v);
int tmp=dp[v][]+E[i].w;
if(biggest<=tmp)
{
bigger=biggest;
biggest=tmp;
}
else if(bigger<tmp)
{
bigger=tmp;
}
}
dp[u][]=biggest;
dp[u][]=bigger;
}
void dfs2(int u)
{
for(int i=V[u].head;~i;i=E[i].next)
{
int v=E[i].v;
dp[v][]=max(dp[u][],dp[v][]+E[i].w==dp[u][] ? dp[u][] : dp[u][])+E[i].w;
dfs2(v);
}
}
int main()
{int n;
scanf("%d",&n);
init();
for(int v=;v<=n;v++)
{
int u,w;
scanf("%d%d",&u,&w);
add_edge(u,v,w);
}
dfs1();
dp[][]=;
dfs2();
for(int i=;i<=n;i++)
{
printf("%d\n",max(dp[i][],dp[i][]));
}
return ;
}

第二题:电视转播

题目描述

一个电视网络计划转播一场重要的足球比赛。网络中的传输点和接收点(即用户)可以表示一棵树。这棵树的根是一个传输点,它将转播比赛。树的叶节点是可能要接受这场比赛的用户(他当然可以选择不看比赛,这样就不要交款)。其他非根节点,非叶节点的中间节点为数据的中转站。将一个信号从一个传输点传到另一个传输点的花费是给定的。整个转播的费用就是每一个传输费用的总和。每一个用户(叶节点)都准备付一定的钱来看这场比赛。电视网络公司要决定是否要给这个用户提供电视信号。例如:给一个节点传输信息的花费太大,而他愿意的付款也很少时,网络公司可能选择不给他转播比赛。写一个程序,找到一个传输方案使最多的用户能看到转播比赛,且转播的费用不超过所有接收信号用户的交款。

输入

输入文件的第一行包含两个整数N和M(2<=N<=3000,1<=M<=N-1)。N,M表示分别表示树的节点数和想观看比赛的用户数。树的根节点用1表示,中间节点的标号为2~N-M,用户的节点标号为N-M+1~N。接下来的N-M行表示传输点的信息(依次是节点1,2……): K A1 C1 A2 C2 …… Ak Ck 表示一个传输点将信号传给K个用户,每一个包含两个数A和C,A表示传输点或用户的节点号,C表示传输的花费。最后一行含有用户的数据,有M个整数表示他们看这场比赛愿意的付费。

输出

仅一行包含一个整数,最大的用户数。

样例数据

样例输入 样例输出

5 3

2 2 2 5 3

2 3 2 4 3

3 4 2

2

5 3

2 2 2 5 3

2 3 2 4 3

4 4 2

3

9 6

3 2 2 3 2 9 3

2 4 2 5 2

3 6 2 7 2 8 2

4 3 3 3 1 1

5

题目分析

注意到了题目结尾处“找到一个传输方案使最多的用户能看到转播比赛,且转播的费用不超过所有接收信号用户的交款”,这种熟悉的感觉——一定是01背包问题,一定是这样的!用户就相当于是物品,传输费用相当于是代价,总交款就是背包的容量......于是,题目类型确定:DP+01背包问题。

定义一个f[MAXN][MAXN]数组,f[l][m]表示:在根节点为l的子树中,将m个用户接入客户端所能得到的最大报酬。DP方程应该是比较好理解的,树形DP套上一个01背包的思路就行了:f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-edge[i].w)

STD Code

#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<algorithm> #define MAXN 3010
#define INF 0x3f3f3f3f using namespace std; struct node
{
int v;
int va;
}; vector <node> tu[MAXN];
int mo[MAXN],f[MAXN][MAXN],get[MAXN];
int n,m; void add(int u,int v,int va)
{
node p;
p.v=v;
p.va=va;
tu[u].push_back(p);
}
void init()
{
int tmp_1,tmp_2,tmp_3;
scanf("%d%d",&n,&m);
for(int i=;i<=n-m;i++)
{
scanf("%d",&tmp_1);
for(int j=;j<=tmp_1;j++)
{
scanf("%d%d",&tmp_2,&tmp_3);
add(i,tmp_2,tmp_3);
}
}
for(int i=n-m+;i<=n;i++)
{
scanf("%d",&mo[i]);
}
}
int dp(int x)
{
if(x>n-m)
{
f[x][]=mo[x];
get[x]=;
return get[x];
}
for(unsigned int i=;i<tu[x].size();i++)
{
get[x]+=dp(tu[x][i].v);
}
for(unsigned int i=;i<tu[x].size();i++)
{
for(int j=get[x];j>=;j--)
{
for(int k=min(j,get[tu[x][i].v]);k>=;k--)
{
f[x][j]=max(f[x][j],f[tu[x][i].v][k]+f[x][j-k]-tu[x][i].va);
}
}
}
return get[x];
}
int main()
{
init();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
f[i][j]=-INF;
}
dp();
int ans=;
for(int i=;i<=m;i++)
{
if(f[][i]>=)
{
ans=max(ans,i);
}
}
printf("%d",ans);
return ;
}

第三题:叶子的颜色(CQOI 2009)

题目描述

给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。

对于每个叶结点u,定义c[u]为从根结点到u的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。

输入

第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。

输出

仅一个数,即着色结点数的最小值。

样例数据

样例输入 样例输出

5 3

0

1

0

1 4

2 5

4 5

3 5

2

题目分析

在本题当中,题目并没有给出树的根节点,于是首先想到:第一步一定是找根......好吧,这个思路是错的,在最后我们会发现:无论选择哪一个节点作为根,都不会影响答案以及染色状态。比如,当前选择一个节点x为根有最优解,y为它某一个的儿子节点,x和y不会同色——同色必然不为最优解,如果不同色,y转化为x的儿子,这样必然不会影响最优解。

由于发现在一个子树中,不可能存在两个颜色不同的点未满足条件,我们可以进行如下定义:
f[u][0]表示以u为根节点的子树中不存在未满足条件的最小染色节点数;

f[u][1]表示以u为根节点的子树中存在被染色为0的节点不满足条件时的最小染色节点数;

f[u][2]表示以u为根节点的子树中存在被染色为1的节点不满足条件时的最小染色节点数;

对此:tmp_1=∑(f[v][1]);   tmp_2=∑(min(f[v][0],f[v][1]));   tmp_3=∑(min(f[v][0],f[v][2]));

由此可得:f[u][0]=min(tmp_1,tmp_2+1,tmp_3+1);   f[u][1]=min(tmp_2,tmp_3+1);   f[u][2]=min(tmp_2+1,tmp_3);

STD Code

#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector> #define MAXN 10010
#define INF 0x3f3f3f3f using namespace std; vector <int> vec[MAXN]; int n,m;
int color,f[MAXN][];
bool vis[MAXN]; void dp(int u)
{
vis[u]=;
int tmp_1=,tmp_2=,tmp_3=;
bool flag=;
for(unsigned int i=;i<vec[u].size();i++)
{
int v=vec[u][i];
if(vis[v])
continue;
dp(v);
tmp_1+=f[v][];
tmp_2+=min(f[v][],f[v][]);
tmp_3+=min(f[v][],f[v][]);
flag=;
}
if(flag)
{
f[u][]=min(tmp_1,min(tmp_2+,tmp_3+));
f[u][]=min(tmp_2,tmp_3+);
f[u][]=min(tmp_2+,tmp_3);
}
}
int main()
{
int u,v;
scanf("%d%d",&n,&m);
memset(f,INF,sizeof(f));
for(int i=;i<=m;i++)
{
scanf("%d",&color);
f[i][]=;
f[i][color+]=;
}
for(int i=;i<=n-;i++)
{
scanf("%d%d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dp(n);
printf("%d\n",f[n][]);
return ;
}

Time : 2017-04-04

[SinGuLaRiTy] 树形DP专项测试的更多相关文章

  1. HDU 1561 树形DP入门

    The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  2. hdu 1561 The more, The Better(树形dp,基础)

    The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  3. HDU 5834 [树形dp]

    /* 题意:n个点组成的树,点和边都有权值,当第一次访问某个点的时候获得利益为点的权值 每次经过一条边,丢失利益为边的权值.问从第i个点出发,获得的利益最大是多少. 输入: 测试样例组数T n n个数 ...

  4. 动态规划——树形dp

    动态规划作为一种求解最优方案的思想,和递归.二分.贪心等基础的思想一样,其实都融入到了很多数论.图论.数据结构等具体的算法当中,那么这篇文章,我们就讨论将图论中的树结构和动态规划的结合——树形dp. ...

  5. app专项测试自动化测试方法思路与实现

    秉着个人意愿打算把python+rf接口自动进行彻底结束再做些其它方面的输出~但事与愿违,但领导目前注重先把专项测试方面完成,借此,先暂停python+rf(主要是与Jenkins集成+导入DB+微信 ...

  6. 树形动态规划(树形DP)入门问题—初探 & 训练

    树形DP入门 poj 2342 Anniversary party   先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...

  7. HDU 1561 The more, The Better 经典树形DP

    The more, The Better Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  8. 【bzoj1369】[Baltic2003]Gem 树形dp

    题目描述 给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小. 输入 先给出一个数字N,代表树上有N ...

  9. HDU 1561 The more, The Better【树形DP/有依赖的分组背包】

    ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物.但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先 ...

随机推荐

  1. Android中使用开源框架EventBus3.0实现Fragment之间的通信交互

    1.概述 在之前的博文中简单介绍过如何实现fragment之间的信息交互:<Android中Fragment与Activity之间的交互(两种实现方式)>,今天继续给大家介绍一种可以实现此 ...

  2. 在vim中搜索单词

    基本的搜索: /pattern    向前搜索 ?pattern   向后搜索 n               向前重复搜索 N               向后重复搜索 搜索并替换 :%s/sear ...

  3. ROM及其他知识

     ROM--Read Only Memory 中文意思是:只读存贮器  以前的游戏机用的都是卡带,里面是一块或几块集成电路芯片,游戏程序就是在生产厂家一次性写入这几块芯片,以后用户玩游戏的时候只能读出 ...

  4. HQL查询——查询返回对象类型分析

    关于HQL查询,我们可以结合hibernate的API文档,重点围绕org.hibernate.Query接口,分析其方法,此接口的实例对象是通过通过session.对象的creatQuery(Str ...

  5. canvas小球动画原理

    随着html5发展,canvas标签作为h5革命性的发展标志也越来越流行.canvas标签的强大之处,不仅在于它可以作为一个独立的画布,也可以利用canvas做一些动画而不用导入flash文件.同时, ...

  6. lucene倒排索引缓冲池的细节

    倒排索引要存哪些信息   提到倒排索引,第一感觉是词到文档列表的映射,实际上,倒排索引需要存储的信息不止词和文档列表.为了使用余弦相似度计算搜索词和文档的相似度,需要计算文档中每个词的TF-IDF值, ...

  7. java装箱跟拆箱解析

    /** * 在jdk1.5之后,java为基本数据类型到对应的应用数据类型提供了自动拆箱装箱操作 * 不管是自动拆箱还是自动装箱都是应用数据类型有的方法,基本数据类型是没有任何方法可调用的 *从概念上 ...

  8. 008 Java集合浅析3

    在前面的几节里,本教程从整体架构上去把握了JDK中的集合框架,并简单分析了其中Collection组的顶级接口,知道Collection接口的常见直接子接口有List.Set和Queue,并就这三个子 ...

  9. 基于 Koa平台Node.js开发的KoaHub.js连接打印机的代码

    最近好多小伙伴都在做微信商城的项目,那就给大家分享一个基于 Koa.js 平台的 Node.js web 开发的框架连接微信易联云打印机接口的代码,供大家学习.koahub-yilianyun 微信易 ...

  10. iOS10构建版本不显示的问题

    iOS10,构建版本问题: 在Xcode中->product->archive,进行相关操作后,upload后没有报错验证也成功的情况下,在开发者账号构建版本号里面迟迟没有显示的原因: i ...