题目大意:

给定n个点的有根树,每条边有边权,每个点有点权w,

你要在k个点上建立伐木场,对于每个没有建伐木场的点x,令与它最近的祖先、有伐木场的点,为y,你需要支付dis(x,y)*w[x]的代价。

选择合适的位置建伐木场,最小化总代价。

n<=100

分析:

f[i][j][k]表示, 以i为根的子树中,离其最近的祖先为j,加上这个点的子树共建了k个伐木场。

树形背包,每个点选择建伐木场,或者不选择建。

注意,无论如何,在x子树y回溯后,是可以在子树根节点y造一个伐木场的。这种情况不要漏掉。

因为0处一定有一个伐木场,所以我们先假设它没有这个伐木场,将k++即可。最后答案就是f[0][0][k] 第二维是0,表示0号点最近的伐木场就是它自己,也就是强制让0点建了一个场。

具体做法:

开始f都是inf

进入每一个节点x,都进行初始化。f[x][x][1~k]=0,f[x][x][1]=0,f[x][ancestors][0]=dis*w[x]

进行树形dp的时候,回溯了y儿子,进行背包前,先新建一个数组g,赋值为inf,否则需要f赋值的时候,f却有可能已经变了。

再把g赋值给f。

背包的时候,分类讨论,一个是在x点建场,还有就是不建场,需要枚举一个最近的祖先。这里,我用的是fa[]数组,不断找父亲,以及disf[]到父亲节点的边的长度。

详见代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
const int K=;
const ll inf=+;
struct node{
int nxt,to;
ll val;
}bian[*N];
int hd[N],cnt;
ll w[N];
int n,k;
ll f[N][N][K];
int fa[N];
ll disf[N];
bool vis[N];
void add(int x,int y,ll z)
{
bian[++cnt].nxt=hd[x];
bian[cnt].to=y;
bian[cnt].val=z;
hd[x]=cnt;
}
void dfs(int x)
{
vis[x]=;
for(int i=;i<=k;i++)
f[x][x][i]=;
ll dis=;
   f[x][x][1]=0;
for(int i=x;i!=;i=fa[i])
{
dis+=disf[i];
f[x][fa[i]][]=dis*w[x];
}// prework for(int i=hd[x];i;i=bian[i].nxt)
{
int y=bian[i].to;
if(!vis[y])//find new son
{
dfs(y);
ll g[N][K];
for(int o=;o<N;o++)
for(int u=;u<K;u++)
g[o][u]=inf;
for(int p=;p<=k;p++)//build one
{
for(int q=;q<p;q++)
{
g[x][p]=min(g[x][p],f[x][x][p-q]+f[y][x][q]);// y's lastest ancestor is x
g[x][p]=min(g[x][p],f[x][x][p-q]+f[y][y][q]);// y's lastest ancestor is itself
}
}
for(int j=x;j!=;j=fa[j])//not
{
for(int p=;p<=k;p++)
{
for(int q=;q<=p;q++)
{
g[fa[j]][p]=min(g[fa[j]][p],f[x][fa[j]][p-q]+f[y][fa[j]][q]);//y's lastest ancestor is fa[j]
g[fa[j]][p]=min(g[fa[j]][p],f[x][fa[j]][p-q]+f[y][y][q]);// y's lastest ancestor is itself
}
}
}
for(int j=x;j!=-;j=fa[j])
{
for(int p=;p<=k;p++)
f[x][j][p]=g[j][p];
}//copy back
}
} }
int main()
{
scanf("%d%d",&n,&k);
k++;//warning!!!
int x,y;
ll z;
for(int i=;i<=n;i++)
{
scanf("%lld%d%lld",&w[i],&y,&z);
fa[i]=y;disf[i]=z;
add(i,y,z);
add(y,i,z);
} fa[]=-;
for(int i=;i<N;i++)
for(int j=;j<N;j++)
for(int o=;o<K;o++)
f[i][j][o]=inf;
dfs();
printf("%lld",f[][][k]);
return ;
}

总结:
1.状态设计值得注意,由于代价和祖先有关,因为这是未来要处理的,不容易直接统计。

所以,就对未来做出承诺,如果在j祖先建伐木场,最小的代价。这里就把这个点运到祖先的代价算上了。

这样,dp到这个祖先的时候,如果决定建造,就可以选择实现承诺。

2.然后,初值都是inf,开始只有f[x][x][1]=0,f[x][ancs][0]=dis*w[x],这就保证了,转移的时候,开始必然有f[x][ancs][0]的贡献。不会漏算。

[IOI2005]River 河流的更多相关文章

  1. [LUOGU] P3354 [IOI2005]Riv 河流

    题目描述 几乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海.这条大河的入海口处有一个村庄--名叫 ...

  2. BZOJ1812 [IOI2005]river

    传送门: 很常规的一道树规,转为左儿子右兄弟. 然后$f[node][anc][K]$表示在node节点上,最近的有贡献祖先在anc上,在node的儿子和兄弟上有k个有贡献节点的最优值. 然后得出以下 ...

  3. [Ioi2005]River

    设f[i][j][k]表示i上游最近的一个伐木场为j且在i所在的子树里共建了k个伐木场(不包含在i的)的最小运费和 设v为u的儿子,dist[u]为u到0号点的距离. 则当i>=j时 f[u][ ...

  4. BZOJ.1812.[IOI2005]Riv 河流(树形背包)

    BZOJ 洛谷 这个数据范围..考虑暴力一些把各种信息都记下来.不妨直接令\(f[i][j][k][0/1]\)表示当前为点\(i\),离\(i\)最近的建了伐木场的\(i\)的祖先为\(j\),\( ...

  5. P3354 [IOI2005]Riv 河流

    树形dp,设f[i][j][k]表示第i个点的子树中选择j个点作为伐木场,而且k是建了伐木场的最浅的i的祖先的情况下,最小的收益. 这种题还要练一下,咕咕 然后转移可以n4方做. // luogu-j ...

  6. 【[IOI2005]Riv 河流】

    趁魏佬去英语演讲了,赶快%%%%%%%%%%%%%%魏佬 基本上是照着魏佬的代码写的 这其实还是一个树上背包 我们用\(dp[i][j][k]\)表示在以\(i\)为根的子树里,我们修建\(k\)个伐 ...

  7. 洛谷P3354 [IOI2005]Riv 河流——“承诺”DP

    题目:https://www.luogu.org/problemnew/show/P3354 状态中要记录一个“承诺”,只需相同承诺之间相互转移即可: 然后就是树形DP的套路了. 代码如下: #inc ...

  8. [IOI2005]Riv 河流

    https://www.zybuluo.com/ysner/note/1300088 题面 有一棵\(n\)个点的树,现在在上面放\(k\)个标记,使得每个点的权值乘上自己到最近的标记祖先的距离的和最 ...

  9. bzoj1812 [IOI2005]riv河流

    题目链接 problem 给出一棵树,每个点有点权,每条边有边权.0号点为根,每个点的代价是这个点的点权\(\times\)该点到根路径上的边权和. 现在可以选择最多K个点.使得每个点的代价变为:这个 ...

随机推荐

  1. c#基础系列1---深入理解值类型和引用类型

    "大菜":源于自己刚踏入猿途混沌拾起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 不知不觉已经踏入坑已10余年之多,对于c#多多少少有一点自己的认识, ...

  2. 浅谈移动端设备标识码:DeviceID、IMEI、IDFA、UDID和UUID

    ---恢复内容开始--- 转:https://www.jianshu.com/p/38f4d1a4763b [心路历程] 最近刚好在思考工作中统计数据所用的标识码产生的数据误差到底有多大,借此机会几番 ...

  3. Docker管理工具 - Swarm部署记录

    之前介绍了Docker集群管理工具-Kubernetes部署记录,下面介绍另一个管理工具Swarm的用法,Swarm是Docker原生的集群管理软件,与Kubernetes比起来比较简单. Swarm ...

  4. 一个数据表通过另一个表更新数据(在UPDAT语句中使用FROM子句)

    在sql server中,update可以根据一个表的信息去更新另一个表的信息. 首先看一下语法: update A SET 字段1=B表字段表达式, 字段2=B表字段表达式   from B WHE ...

  5. 安装Visual Studio 2013以及简单使用

    首先,在网上找到安装Visual Studio 2013的教程以及相关软件资源http://jingyan.baidu.com/article/09ea3ede3b2496c0afde3944.htm ...

  6. 20135323符运锦----LINUX第二次实践:内核模块编译

    Linux实践二--模块 一.知识点总结 ①Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合.之所以提供模块机制,是因为Linux本身是一个单内核.单内核由于所有内容都集成在一起,效率 ...

  7. b总结

    Beta 答辩总结 评审表 组名 格式 内容 ppt 演讲 答辩 总计 天机组 15 15 13 15 14 72 PMS 16 16 15 16 16 79 日不落战队 16 17 17 17 17 ...

  8. atcoder B - Frog 2 (DP)

    B - Frog 2 Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement There a ...

  9. Linux (centos7) 防火墙命令

    防火墙配置 CentOS 7默认使用的是firewall作为防火墙,这里改为iptables防火墙. firewall操作: # service firewalld status; #查看防火墙状态 ...

  10. logback基本入门

    1. logback的定义 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: logback-core:其它 ...