POJ 1849 - Two - [DFS][树形DP]
Time Limit: 1000MS Memory Limit: 30000K
Description
Heavy snow covered the city so the mayor Milan gave to the winter-service a list of streets that have to be cleaned of snow. These streets are chosen such that the number of streets is as small as possible but still every two intersections to be connected i.e. between every two intersections there will be exactly one path. The winter service consists of two snow plovers and two drivers, Mirko and Slavko, and their starting position is on one of the intersections.
The snow plover burns one liter of fuel per meter (even if it is driving through a street that has already been cleared of snow) and it has to clean all streets from the list in such order so the total fuel spent is minimal. When all the streets are cleared of snow, the snow plovers are parked on the last intersection they visited. Mirko and Slavko don’t have to finish their plowing on the same intersection.
Write a program that calculates the total amount of fuel that the snow plovers will spend.
Input
Each of the next N-1 lines contains three integers: A, B and C, meaning that intersections A and B are directly connected by a street and that street's length is C meters, 1 <= C <= 1000.
Output
Sample Input
5 2
1 2 1
2 3 2
3 4 2
4 5 1
Sample Output
6
题意:
这个城市由节点和连接节点的街道组成,街道是双向的。
此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小。
现有两台相同的扫雪机S和M,它们的起点在同一个节点上。
所有被确定的街道必须至少被一台扫雪机经过,才能完成清扫任务,完成任务后S和M可以在原地停下,不必集合到某一点。
扫雪机的行进是需要耗费油量的(即使扫雪机行驶的是已被扫净的街道),因此扫雪机行进的总距离越小越好,你需要计算两台扫雪机完成任务的最小总行进距离。
题解:
我们知道,树都有一条直径,那么,
假设从这棵树上的某一点出发,遍历完所有的点之后回到该点需要走的边数是个固定值,为这棵子树的边数的两倍;
具体为什么,不带方向的树,任意一点都可作为树根,那么把选到的那一点作为树根,进行DFS模拟,很容易看出遍历完所有的点需要两倍边长总和;
但是根据我们题目的要求,其实遍历到最后剩下的一个点后,是没有必要返回的;
那么答案应该为边数的两倍减去始末这两点之间的距离,要使结果最优,必然要使这两点之间的距离尽可能的大,而树上两点之间的距离的最大值便为树的直径;
也就是说,如何寻找一条最短的路径遍历完一整棵树,应该以树的直径一端为起点,另一端为终点进行遍历。
回到本题,两辆车分担工作,那么就可以将两辆车的终点分别设为树直径的两个端点,而行进路线同上一模一样,那么这两辆车的起点在哪儿就无关紧要了:
①如果我们的起始点在直径上,那么很显然的,两辆车分别朝两个方向走,遇到直径上的分叉,就要花费分叉的总长*2的路程来遍历这些分叉,而直径上的边都只需要走一遍即可
②而如果我们的起始点不在直径上,那么他们两辆车只需要先把这个分叉遍历一遍,最后回到直径上,那么又可以按①的过程,两辆车朝两边走。
综上,我们就可以知道,这样需要走过的总路程 ans = "直径长度" + 2 * "所有分叉上边的长度总和",换句话说,就是ans = 2 * "所有边的长度总和" - "直径长度";
至于,求树的直径,我们可以有两种方法:
参考:http://blog.csdn.net/tc_to_top/article/details/47002255

首先是两次DFS的过程:
#include<cstdio>
#include<vector>
#define MAXN 100000+5
using namespace std;
struct Edge{
int u,v,w;
};
vector<Edge> adj[MAXN];
int n,s,d[MAXN],sum;
void dfs(int now,int par)
{
for(int i=;i<adj[now].size();i++)
{
Edge edge=adj[now][i];
int next=edge.v;
if(next==par) continue;
d[next]=d[now]+edge.w;
dfs(next,now);
}
}
int main()
{
while(scanf("%d%d",&n,&s)!=EOF)
{
sum=;
for(int i=;i<=n;i++) adj[i].clear();
for(int i=;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
sum+=w;
adj[u].push_back((Edge){u,v,w});
adj[v].push_back((Edge){v,u,w});
}
d[s]=;
dfs(s,-);
int max=,max_i;
for(int i=;i<=n;i++)
{
if(max<d[i])
{
max=d[i];
max_i=i;
}
}
d[max_i]=;
dfs(max_i,-);
int diameter=;
for(int i=;i<=n;i++) if(diameter<d[i]) diameter=d[i];
printf("%d\n",*sum-diameter);
}
}
至于树形DP的方法,思维其实和两次DFS异曲同工,通过DFS可以得到任何一个点的dp[i][0]和dp[i][1]:
dp[i][0]存储点i延伸出去所能达到的最远长度,dp[i][1]存储点i从另一完全不同的路线(意思是一条重合的边都不能有)延伸出去所能到达的最远长度(称其为次远长度);
那么显然,所有点中,只有在树直径上的那些个点的dp[i][0]+dp[i][1]是最大的,而此时dp[i][0]+dp[i][1]的值即为树的直径长度。
#include<cstdio>
#include<cstring>
#include<vector>
#define MAXN 100000+5
using namespace std;
struct Edge{
int u,v,w;
};
vector<Edge> adj[MAXN];
int n,s,sum,diameter;
int dp[MAXN][];
void dfs(int now,int par)
{
for(int i=;i<adj[now].size();i++)
{
Edge edge=adj[now][i];
int next=edge.v;
if(next==par) continue;
dfs(next,now);
if(dp[now][] < dp[next][]+edge.w) // ( "其某个孩子的最大"+"其与孩子的距离" ) > "最大" > "次大"
{
dp[now][] = dp[now][];
dp[now][] = dp[next][] + edge.w;
}
else if(dp[now][] < dp[next][]+edge.w) // "最大" > ( "其某个孩子的最大"+"其与孩子的距离" ) > "次大"
{
dp[now][] = dp[next][]+edge.w;
}
}
if(diameter<dp[now][]+dp[now][]) diameter=dp[now][]+dp[now][];
}
int main()
{
while(scanf("%d%d",&n,&s)!=EOF)
{
diameter=sum=;
for(int i=;i<=n;i++) adj[i].clear();
memset(dp,,sizeof(dp));
for(int i=;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
sum+=w;
adj[u].push_back((Edge){u,v,w});
adj[v].push_back((Edge){v,u,w});
}
dfs(s,-);
printf("%d\n",*sum-diameter);
}
}
POJ 1849 - Two - [DFS][树形DP]的更多相关文章
- poj 2324 Anniversary party(树形DP)
/*poj 2324 Anniversary party(树形DP) ---用dp[i][1]表示以i为根的子树节点i要去的最大欢乐值,用dp[i][0]表示以i为根节点的子树i不去时的最大欢乐值, ...
- POJ 1655.Balancing Act 树形dp 树的重心
Balancing Act Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14550 Accepted: 6173 De ...
- POJ 2342 - Anniversary party - [树形DP]
题目链接:http://poj.org/problem?id=2342 Description There is going to be a party to celebrate the 80-th ...
- POJ - 3162 Walking Race 树形dp 单调队列
POJ - 3162Walking Race 题目大意:有n个训练点,第i天就选择第i个训练点为起点跑到最远距离的点,然后连续的几天里如果最远距离的最大值和最小值的差距不超过m就可以作为观测区间,问这 ...
- POJ 2486 Apple Tree(树形DP)
题目链接 树形DP很弱啊,开始看题,觉得貌似挺简单的,然后发现貌似还可以往回走...然后就不知道怎么做了... 看看了题解http://www.cnblogs.com/wuyiqi/archive/2 ...
- POJ 3162 Walking Race 树形DP+线段树
给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...
- poj 2342 Anniversary party 树形DP入门
题目链接:http://poj.org/problem?id=2342 题意:一家公司有1 <= N <= 6 000个职工,现要组织一些职工参加晚会,要求每个职工和其顶头上司不能同时参加 ...
- 杭电OJ——1011 Starship Troopers(dfs + 树形dp)
Starship Troopers Problem Description You, the leader of Starship Troopers, are sent to destroy a ba ...
- POJ 3162.Walking Race 树形dp 树的直径
Walking Race Time Limit: 10000MS Memory Limit: 131072K Total Submissions: 4123 Accepted: 1029 Ca ...
随机推荐
- ios的两种界面跳转方式
1.在界面的跳转有两种方法,一种方法是先删除原来的界面,然后在插入新的界面,使用这种方式无法实现界面跳转时的动画效果. if(self.rootViewController.view.supervie ...
- PHP 免费获取手机号码归属地
一.淘宝网API API地址: http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15850781443 参数: tel:手机号码 返回 ...
- java命令行操作
一直使用eclipse操作java程序,但RMI程序需要命令行操作,故研究了下java的命令行操作. javac 用于编译.java文件,生成.class文件 假设文件夹dir下有pa.java和a. ...
- 【代码审计】iCMS_v7.0.7 apps.admincp.php页面存在SQL注入漏洞分析
0x00 环境准备 iCMS官网:https://www.icmsdev.com 网站源码版本:iCMS-v7.0.7 程序源码下载:https://www.icmsdev.com/downloa ...
- 【安全开发】PHP安全编码规范
申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%8 ...
- 免费SVN、Git项目托管主机推荐
Unfuddle 200MB的免费空间,界面友好,特性丰富,支持Git,但只能一个账户一个用户并且只允许一个项目,付费服务相对来说价格偏高 CodeSpaces 500MB,一个账户两个免费用户,付费 ...
- Django 访问数据库
通过命令行方式访问数据库: [root@localhost web]$ python manage.py shell # 进入交互模式(先安装ipython) In [1]: from blog.mo ...
- Eclipse cdt debug时‘Error while launching command: gdb.exe --version’
1. 下载gdb,网上很多可以下载一个,解压放在mingw/bin下,由于该目录以在path制定,在CMD下,gdb -version会显示当前gdb版本信息. 2.按照该文档配置即可实现debug
- 微信小程序的wx-charts插件
还有就是可以使用一些小程序的插件,比如wx-charts. 先来看一下网上对这个插件的评价: 目前在github上有1804颗星,使用的比较广泛. github地址:https://github.co ...
- 【Java并发编程三】闭锁
1.什么是闭锁? 闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流.常见类型的Synchronizer包括信号量.关卡和闭锁). ...