[HNOI/AHOI2018]道路

题目链接

P4438 [HNOI/AHOI2018]道路

题目大意

W 国的交通呈一棵树的形状。W 国一共有 n − 1 n - 1 n1个城市和 n n n个乡村,其中城市从 1 1 1 n − 1 n - 1 n1 编号,乡村 1 1 1 n n n编号,且 1 1 1号城市是首都。道路都是单向的,本题中我们只考虑从乡村通往首都的道路网络。对于每一个城市,恰有一条公路和一条铁路通向这座城市。对于城市i, 通向该城市的道路(公路或铁路)的起点,要么是一个乡村,要么是一个编号比 i i i大的城市。 没有道路通向任何乡村。除了首都以外,从任何城市或乡村出发只有一条道路;首都没有往 外的道路。从任何乡村出发,沿着唯一往外的道路走,总可以到达首都。
W 国的国王小 W 获得了一笔资金,他决定用这笔资金来改善交通。由于资金有限,小 W 只能翻修 n − 1 n - 1 n1条道路。小 W 决定对每个城市翻修恰好一条通向它的道路,即从公路和铁 路中选择一条并进行翻修。小 W 希望从乡村通向城市可以尽可能地便利,于是根据人口调 查的数据,小 W 对每个乡村制定了三个参数,编号为 i i i的乡村的三个参数是 a i a_i ai b i b_i bi c i c_i ci。假设 从编号为 i i i的乡村走到首都一共需要经过 x x x条未翻修的公路与 y y y条未翻修的铁路,那么该乡村 的不便利值为
c i ⋅ ( a i + x ) ⋅ ( b i + y ) c_i \cdot (a_i + x) \cdot (b_i + y) ci(ai+x)(bi+y)
在给定的翻修方案下,每个乡村的不便利值相加的和为该翻修方案的不便利值。 翻修 n − 1 n - 1 n1条道路有很多方案,其中不便利值最小的方案称为最优翻修方案,小 W 自然 希望找到最优翻修方案,请你帮助他求出这个最优翻修方案的不便利值。
输入输出格式
输入格式:
第一行为正整数 n n n
接下来 n − 1 n - 1 n1行,每行描述一个城市。其中第 i i i行包含两个数 s i , t i s_i,t_i si,ti s i s_i si表示通向第 i i i座城市 的公路的起点, t i t_i ti表示通向第i座城市的铁路的起点。如果 s i &lt; 0 s_i &lt;0 si<0,那么存在一条从第 s i s_i si座城 市通往第 i i i座城市的公路,否则存在一条从第 − s i -s_i si个乡村通往第i座城市的公路; t i t_i ti类似地,如 果 t i &lt; 0 t_i &lt;0 ti<0,那么存在一条从第 t i t_i ti座城市通往第i座城市的铁路,否则存在一条从第 − t i -t_i ti个乡村通 往第 i i i座城市的铁路。
接下来 n n n行,每行描述一个乡村。其中第 i i i行包含三个数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,其意义如题面所示。
输出格式:
输出一行一个整数,表示最优翻修方案的不便利值。
输入输出样例
输入样例#1:
6
2 3
4 5
-1 -2
-3 -4
-5 -6
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
输出样例#1:
54
输入样例#2:
9
2 -2
3 -3
4 -4
5 -5
6 -6
7 -7
8 -8
-1 -9
1 60 1
1 60 1
1 60 1
1 60 1
1 60 1
1 60 1
1 60 1
1 60 1
1 60 1
输出样例#2:
548
输入样例#3:
12
2 4
5 3
-7 10
11 9
-1 6
8 7
-6 -10
-9 -4
-12 -5
-2 -3
-8 -11
53 26 491
24 58 190
17 37 356
15 51 997
30 19 398
3 45 27
52 55 838
16 18 931
58 24 212
43 25 198
54 15 172
34 5 524
输出样例#3:
5744902


心路历程 如果时间不够,请直接跳到正文部分_(ˇωˇ」∠)_

一开始拿到这份题的时候,坚持在10分钟之内看完题,之后再坚持在10分钟之内想到思路。坚持了20分钟之后,还是一点思路都没有,甚至觉得自己是脑子抽了,为什么会想到写这一道题,读题两小时,暴毙一小时。。。

这份题是在昨天的时候学习树形DP的时候,在洛谷上找到的一道题,一开始看到的时候,就是想退缩,我的天,那么长的题面(之后就去看了题解,请不要像我一样

之后昨天晚上的晚自习就一直在看题解,awsl(啊,我死了。),就是各种题解,各种代码,各种复杂,各种看不懂。。。。。。。。代码中的define都能占到将近10行,哦买噶的,彻底暴毙。

但是,最后,我还是决定肝了两天,决心要A掉它,就在今天刚刚的时候第二次提交在这里插入图片描述
哇哇哇哇哇哇哇哇哇哇,内心激动!!!!!!!!!!!!
但是,毕竟不是第一次,所以有点难受。
这里就要点一下警告的重要性!!!(也就是代码规范性的问题)

第一次:因为没有在long long的函数中返回值,所以全部挂掉
在这里插入图片描述
之后在代码中改掉了一些细节,之后就全部都A掉了!!!!!!!!

细节决定成败。


正文

理解题意

如果你仔细读题的话,可以从题目中提取出来的一些信息:

  1. 是一棵树, 节点是城市(n-1)和乡村(n)
  2. 一号城市是根节点同时也是首都。
  3. 道路都是单向的(从乡村通往城市)
  4. 父亲节点一定比子节点小,每一个节点都只有一个出度(每一个点都只有一个父亲节点)
  5. 一共可以翻修(n-1)条道路,并且是对通向每一个城市进行翻修
  6. 用样例可以把图画出来(可以发现图是一个满二叉树)

这个二叉树具有一定的性质:

  1. 所有的乡村都是叶子节点,所有的城市都是非叶子节点,首都是根节点。
  2. 公路都是由左儿子发出来的,铁路都是由右儿子发出来的;

那么在把题目简化一下就是:对于每一个非叶子节点都可以翻修一条通向他的边,求叶子节点到根节点的最小的花费代价(我们把不便利值叫做代价,不便利值的求法在题目的正文部分)

在理解题目之后要做的,要想的出来的,叫做思路。

思路

在这里插入图片描述
上图是样例一的示意图。
题目中说的是对通向每一个城市的公路或者是铁路其中的一条进行翻修,此时我觉得最浅显的思路就是我们尝试着把每一条公路和铁路全部分别修一遍,之后确定修这条路或者是不修这条路的价值是否发生了改变,之后找到最小的价值。

因为这是在树上的问题,所以我们考虑一下按照树上的套路解题:对于每一个节点x,先递归在它的每个子节点进行DP,在回溯时,从子节点向节点x进行状态转移。

对于每一个节点都有两种操作:

  1. 修建左儿子的公路,这样子的话右儿子的铁路就剩了下来;
  2. 修建右儿子的铁路,这样子的话左儿子的公路就剩了下来;

这样子的话状态数过多,考虑DP(记忆化搜索)。

之后这道题,我们就完美的解决了!!!
对了,对于题解上的卡空间的操作和memset操作,我都没有,还是玄学的过了,啦啦啦啦(~ ̄▽ ̄)~

code

//Author:melody
#include<bits/stdc++.h>
using namespace std;

const int nn=20002;
const int mm=43;
typedef long long ll;

int n;
int ls[nn],rs[nn];
ll f[nn][mm][mm];/*f[i][j][k]标记当前点i在有j条公路,k条铁路时的最小价值*/
int a[nn],b[nn],c[nn];

inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}

ll dfs(int now,int x,int y)/*now表示目前到达的节点,x表示公路有多少条没有维修,y表示铁路有多少条没有维修*/
{
	if(now>n)	return	(ll)c[now-n]*(a[now-n]+x)*(b[now-n]+y);
	if(f[now][x][y])	return f[now][x][y];
	f[now][x][y]=dfs(ls[now],x,y)+dfs(rs[now],x,y+1);/*铁路没有维修*/
	return f[now][x][y]=min(f[now][x][y],dfs(ls[now],x+1,y)+dfs(rs[now],x,y));/*公路没有维修*/
	/*注意注意:这里一定要有return*/
}

int main()
{
	n=read();
	for(int i=1;i<n;++i)
	{
		ls[i]=read(), rs[i]=read();
		if(ls[i]<0)	ls[i]=-ls[i]+n;
		if(rs[i]<0)	rs[i]=-rs[i]+n;/*给乡村重新编号,也就说乡村的编号是n+1~2*n*/
	}	
	for(int i=1;i<=n;++i)	a[i]=read(),b[i]=read(),c[i]=read();
	printf("%lld\n",dfs(1,0,0));
	return 0;
} 

反思

在平时做题的时候也是会有在函数里没有return的操作,而且dev的容错性很强的,一般来说有错误是不会被发现的,那么就只能需要自己下一点功夫了。

  1. 谨记在非void的函数中注意有没有返回值,除了自己排除的一些特殊情况外(这些是在函数的开头就已经有返回值的),其他的有没有返回值,比如说在函数的末尾,和有一些引用的函数的地方是不是需要函数的返回值;
  2. 适当的可以用MinGW进行调试,或者是说用GDB进行调试,这样子的话,代码的问题还是很容易就暴露出来的;
  3. 养成良好的代码规范,记得函数中随时返回。
  4. 注意注意:细节决定成败!!

你要克服的是你的虚荣心,是你的炫耀欲,你要对付的是你时刻想要出风头的小聪明。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值