树形DP-----HDU4003 Find Metal Mineral
Find Metal Mineral
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others) Total Submission(s): 2371 Accepted Submission(s): 1079
3 1 1 1 2 1 1 3 1 3 1 2 1 2 1 1 3 1
In the first case: 1->2->1->3 the cost is 3; In the second case: 1->2; 1->3 the cost is 2;
dp[pos][num]表示以pos为根节点的子树下,用去num个机器人,所得到的最小值
特别的是当num==0的时候,dp[pos][0]表示用一个机器人去走完所有子树,最后又回到pos这个节点
状态转移:dp[pos][num]=min∑{dp[pos_j][num_j]+w_j},pos_j是pos的所有儿子,
当num_j==0的时候,和别的时候不同,特别处理一下就好。
状态转移并不难,最精华的,我不认为是状态转移,而是转移时使用的那个“分组背包”思想。
使用一维数组的“分组背包”伪代码如下:
for 所有的组i
for v=V..0
for 所有的k属于组i
f[v]=max{f[v],f[v-c[k]]+w[k]}
#include <iostream>
#include <cstdio>
#include <cstring> using namespace std; struct Edge{
int to;
int value;
int next;
}edge[*];
int head[],dp[][];
int N,S,K,total; inline int MIN(int a,int b)
{
if(a<b) return a;
return b;
}
void addEdge(int start,int end,int value)
{
edge[total].to=end;edge[total].value=value;
edge[total].next=head[start];head[start]=total++; edge[total].to=start,edge[total].value=value;
edge[total].next=head[end],head[end]=total++;
} void DP(int source,int pre)
{
for(int i=head[source];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(to==pre) continue;
DP(to,source);
for(int j=K;j>=;j--)
{
dp[source][j]+=dp[to][]+*edge[i].value;
for(int k=;k<=j;k++)
dp[source][j]=MIN(dp[source][j],dp[source][j-k]+dp[to][k]+k*edge[i].value);
}
}
} int main()
{
while(scanf("%d %d %d",&N,&S,&K)!=EOF)
{
memset(dp,,sizeof(dp));
memset(head,-,sizeof(head));
total=; int x,y,cost;
for(int i=;i<N;i++)
{
scanf("%d %d %d",&x,&y,&cost);
addEdge(x,y,cost);
}
DP(S,-);
printf("%d\n",dp[S][K]);
}
return ;
}
思路和其他人差不多,谈谈自己是怎么理解的吧:
首先,用两数组建立无向树,还是第一次,费了不少功夫。定义一个结构代表边edge[i]={to,next,value},其中edge[i]表示第i条边,to是边的末点,value就不说了,next代表的是同一父节点边方向相同的与此边相邻的左边的边的编号,其中最左是-1。以此为基础建立一个边数组(注意数组大小是顶点数的两倍),还有用一个head[i]数组来表示最右边的边的编号。
解释这一段代码,当时写的理解的不是很好:
void DP(int source,int pre)
{
for(int i=head[source];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(to==pre) continue;
DP(to,source);
for(int j=K;j>=;j--)
{
dp[source][j]+=dp[to][]+*edge[i].value;
for(int k=;k<=j;k++)
dp[source][j]=MIN(dp[source][j],dp[source][j-k]+dp[to][k]+k*edge[i].value);
}
}
}
第一层for循环查找与节点source相连的各条边,to是此边对应的末点,若此边是叶子节点则to==pre(由于节点最左边的边访问完以后,只有到他父节点的边,这里只能用continue,因为不确定访问的顺序,return不合适),递归访问子节点此时应用01背包,由于dp[source][0]比较特殊单独处理,一棵子树对应一个分组,dp[source][j]=MIN(dp[source][j],dp[source][j-k]+dp[to][k]+k*edge[i].value);也比较好理解。
树形DP-----HDU4003 Find Metal Mineral的更多相关文章
- 【树形dp】Find Metal Mineral
[HDU4003]Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (J ...
- HDU4003 Find Metal Mineral 树形DP
Find Metal Mineral Problem Description Humans have discovered a kind of new metal mineral on Mars wh ...
- HDU-4003 Find Metal Mineral 树形DP (好题)
题意:给出n个点的一棵树,有k个机器人,机器人从根节点rt出发,问访问完整棵树(每个点至少访问一次)的最小代价(即所有机器人路程总和),机器人可以在任何点停下. 解法:这道题还是比较明显的能看出来是树 ...
- HDU4003 Find Metal Mineral
看别人思路的 树形分组背包. 题意:给出结点数n,起点s,机器人数k,然后n-1行给出相互连接的两个点,还有这条路线的价值,要求最小花费 思路:这是我从别人博客里找到的解释,因为很详细就引用了 dp[ ...
- HDU-4003 Find Metal Mineral (树形DP+分组背包)
题目大意:用m个机器人去遍历有n个节点的有根树,边权代表一个机器人通过这条边的代价,求最小代价. 题目分析:定义状态dp(root,k)表示最终遍历完成后以root为根节点的子树中有k个机器人时产生的 ...
- HDU4003Find Metal Mineral[树形DP 分组背包]
Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Other ...
- hdu 4003 Find Metal Mineral 树形DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal miner ...
- hdu 4003 Find Metal Mineral 树形dp ,*****
Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Other ...
- HDU 4003 Find Metal Mineral(分组背包+树形DP)
题目链接 很棒的一个树形DP.学的太渣了. #include <cstdio> #include <string> #include <cstring> #incl ...
- hdu4003详解(树形dp+多组背包)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Find Metal Mineral Time Limit: 2000/1000 MS (Jav ...
随机推荐
- selenium - xpath - 定位
前言: XPath 是一门在 XML 文档中查找信息的语言.XPath 可用来在 XML 文档中对元素和属性进行遍历. 看这里介绍:w3school 首先来看一下xpath常用的语法: 一.xpath ...
- firstModel权限问题
laravel根目录下没有 .env , 执行 cp .env.example .envstorage和bootstrap/cache 没有给写入权限, 执行 chmod -R 777 storage ...
- awk常见基本使用
-F 指定分割符号 print 外层的引号必须是单引号 $n不能被解析 [root@bogon ~]# .txt a:b:c:d a1:b1:c1:d1 a2:b2:c2:d2 a_: :c:dddd ...
- JavaScript笔记——面向对象与原型
JavaScript也是一门面向对象的语言.面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,JavaScript竟然没有class,因此它的面向对象也 ...
- Handler消息传送机制
一.什么是UI线程 当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread). 主要负责处理与UI相关的事件. 二.UI线程存在的问题 出于性能优化考虑,Android的 ...
- mysql字符类型
字符类型 #官网:https://dev.mysql.com/doc/refman/5.7/en/char.html #注意:char和varchar括号内的参数指的都是字符的长度 #char类型:定 ...
- 完整部署OpenStack-memcache配置
一.[root@openstack-server ~]# nova listERROR (CommandError): You must provide a username or user id v ...
- Python Twisted系列教程11:改进诗歌下载服务器
作者:dave@http://krondo.com/your-poetry-is-served/ 译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 诗歌下载服务器 到目前为止,我们已经学习了大 ...
- puclic 页面公共CSS样式
body, div, dl, dt, dd, ul, ol, li, pre, form, fieldset, blockquote, h1, h2, h3, h4, h5, h6,p{ paddin ...
- 如何重置Oracle服务器ILOM中root用户的密码
在ILOM中,root用户的缺省密码是changeme. 为了系统的安全性,通常情况下需要修改root密码. 如果忘了密码,可以参考以下步骤重置root密码. 用串口线连接Oracle服务器ILOM ...