IOI2005 [动态规划 树形DP] 河流
来源:互联网 发布:apache安装包下载地址 编辑:程序博客网 时间:2024/05/16 10:40
背景
安徽省芜湖市集训队练习题
IOI 2005 Rivers(riv)
Description:Amber
Data:Official
Program:JackDavid127
描述
几乎整个Byteland 王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——Bytetown。
在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland 的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在 运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。
注:所有的河流都不会分叉,形成一棵树,根结点是Bytetown。
国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米1分钱。
编一个程序:
1.从文件读入村子的个数,另外要建设的伐木场的数目,每年每个村子产的木料的块数以及河流的描述。
2.计算最小的运费并输出。
格式
输入格式
第一行包括两个数n(2<=n<=100),k(1<=k<=50,且k<=n)。n为村庄数,k为要建的伐木场的数目。除了Bytetown 外,每个村子依次被命名为 1,2,3……n,Bytetown被命名为0。
接下来n行,每行3个整数:
wi——每年 i 村子产的木料的块数。(0<=wi<=10000)
vi——离 i 村子下游最近的村子。(即 i 村子的父结点)(0<=vi<=n)
di——vi 到 i 的距离(千米)。(1<=di<=10000)
保证每年所有的木料流到bytetown 的运费不超过2000,000,000分
50%的数据中n不超过20。输出格式
输出最小花费,精确到分。
样例
样例输入
4 2
1 0 1
1 1 10
10 2 5
1 2 3
样例输出
4
提示
树形动态规划
经典问题
来源
安徽省芜湖市集训队练习题
IOI 2005 Rivers(riv)
Description:Amber
Data:Official
Program:JackDavid127
我想如果就算提示不说这好似树形dp,也很好判断这是(废话= =),好吧这题以我的能力的确是做不出来,然后在网上又跟随了一个错误的领导者…对着错的程序看了半天…最后还是在错误的基础上改对了。改到心累。(左右儿子表示码错,改了好久QAQ)
题目大意可以这样理解:每个节点有一个带有权值的点,这个点可以就地消掉,也可以通过带有权值的边送到它的父亲节点,要求所有点都消掉的最小代价。
首先需要了解的知识点就是多叉树转二叉数,也就是左儿子右兄弟。然后就可以从后往前dp。至于实现从后往前的方法,可以递归实现,也可以实现预处理一下,搜一遍将转换后的二叉树,让每个节点的儿子和兄弟在新序列的后面。两种方法皆可。
至于状态转移方程,用 f[i][j][k] 表示目前考虑第 i 个点, j 表示 i 所有的祖先中离 i 最近并且建了伐木场的节点, i 的儿子以及 i 的兄弟一共建了 k 个伐木场。那么对于 i 这个点就有两种选择:1、不在这个节点建造伐木场。2、在这个节点建。
转移如下:
(在)f[i][j][k]=min(f[i][j][k],f[leftson][i][l]+f[rightson][j][k-l-1])
(不在)f[i][j][k]=min(f[i][j][k],f[leftson][j][l]+f[rightson][j][k-l]+val[i]*(dis i..j))
这里解释一下:
在这个节点建伐木场时, i 的儿子们就可以直接送到 i 节点,因此左儿子为 f[leftson][i][l] ;但 i 的兄弟们无法到达 i ,所以它们依然需要送到 j (离 i 最近并且建了伐木场的节点),因此右儿子为 f[rightson][j][k-l-1] 。另外,由于 i 建造用去一个伐木场,所以方程中的第三维需减一。
不在这里建时,左右儿子都需送到 j ,但还要加上 i 点权 * i..j 的距离。这个很好理解,但是此处不需要处理 i..j 节点中的节点的点权,因为此处只考虑 i 点, i..j 的节点以后可以推出。
代码:
#include<cstdio>#include<cstring>#include<iostream>using namespace std;struct node{ int left,right,num,dis;}gg[105];int dui[105],deep[105],child[105],f[105][105][51],tot,fa[105];const int INF=0x7f7f7f7f;void dfs(int k,int dep)//生成从后往前推的序列{ dui[++tot]=k; deep[k]=dep+gg[k].dis; int i=gg[k].left; while(i) { dfs(i,deep[k]); i=gg[i].right; }}int main(){ int n,K; cin>>n>>K; memset(fa,-1,sizeof fa); for(int i=1;i<=n;i++) { int a,b,c; cin>>a>>b>>c; if(!child[b]) gg[b].left=i; else gg[child[b]].right=i; child[b]=i; gg[i].num=a; gg[i].dis=c; fa[i]=b; }//多叉转二叉 dfs(0,0); memset(f,INF,sizeof f);//需要赋初值 memset(f[0],0,sizeof f[0]);// for(int i=n+1;i>=2;i--) { int now=dui[i]; int le=gg[now].left; int ri=gg[now].right; for(int j=fa[now];j!=-1;j=fa[j])//枚举父亲节点 for(int k=0;k<=K;k++) { for(int l=0;l<=k;l++)//不选 i if(f[le][j][l]!=INF&&f[ri][j][k-l]!=INF) { int add=gg[now].num*(deep[now]-deep[j]); int op=f[le][j][l]+f[ri][j][k-l]+add; f[now][j][k]=min(f[now][j][k],op); } for(int l=0;l<k;l++)//选 i if(f[le][now][l]!=INF&&f[ri][j][k-l-1]!=INF) { int op=f[le][now][l]+f[ri][j][k-l-1]; f[now][j][k]=min(op,f[now][j][k]); } } } printf("%d",f[gg[0].left][0][K]);}
- IOI2005 [动态规划 树形DP] 河流
- IOI2005 RIV 树形DP
- hdu_1054_树形dp动态规划
- 动态规划-树形dp总结
- 动态规划_树形DP
- BZOJ 1812 Ioi2005 riv 树形dp
- 【bzoj 1812】[Ioi2005]riv(树形dp)
- 树形动态规划(树状DP)小结
- 【动态规划】【树形DP】[BZOJ 1040]骑士
- 树形动态规划(树状DP)小结
- 动态规划之树形DP专题
- bzoj 1812: [Ioi2005]riv (树形dp+多叉树转二叉树)
- 树形dp(IOI 2005河流代码理解)
- POJ 1947 Rebuilding Roads(动态规划-树形DP)
- 【动态规划】【树形DP】[UVa 10859]Placing Lampposts
- HDU 5909 Tree Cutting [树形dp+FWT]【动态规划+数学】
- 动态规划(树形DP)之Anniversary party
- 树形动态规划
- Ubuntu14.04 for ROS indigo的安装(电脑配置)
- Android Touch事件传递机制通俗讲解
- 错误的BUG
- 项目 5 - 顺序串算法(3)
- Spring中封装将对象使用注解方式进行封装
- IOI2005 [动态规划 树形DP] 河流
- Centos 6.5 升级gcc6.1
- linux上rsync数据同步
- IOS开发之CollectionViewCell重用产生重叠的处理方法
- Linux系统下的bootloader、Linux内核、root文件系统介绍
- squid 高匿设置
- 【XJOI】NOIP2016提高组冲剌题1 T3 排队
- 欢迎使用CSDN-markdown编辑器
- Spring4 条件注解@Conditional