【观光旅游】解题报告

              Problem 2      观光旅游观光旅游 (photo.pas/c/cpp) 
                             观光旅游观光旅游 

背景背景 
背景背景 

  WZOI 的CJH 教练经常出去旅游 (机房男们那叫一个羡慕啊~~~~~)。当然,CJH 教练的 
旅游可不是观赏风景那么简单…… 

问题描述问题描述 
问题描述问题描述 

  CJH 教练预选了N 个城市,打算去这些城市逛逛,并且要拍出一定质量的照片,第i 个城 
市所能拍出的照片质量为Ci。可是,由于眼光有限,CJH 教练选出的一些城市并不能拍出 
多少精彩的照片,因此,Ci  可能小于零。N                 个城市由一些公路连通,每个城市之间有且只 
有一条路径使其连通。而CJH 教练走的路线比较奇特,在他去的几个城市中,连通任意两 
个城市的路径上的城市,CJH 教练也都要去过才行 (其实就是要求经过的城市连通)。要知 
道,拍出的照片可是要在机房中展览的,CJH  教练可不想让机房男嘲笑自己的拍照水准。 
鉴于机房男的审美观都不咋的,在好照片中混一些烂照片也不是不可以。CJH  教练有些犯 
难,选哪些城市才能让照片总质量最高呢? 
  CJH 教练非常看好你,就把这个光荣而艰巨的任务交给你了! 

输入格式输入格式 
输入格式输入格式 

  输入数据第1 行有1 个正整数N ,表示城市个数。 
  第2 行有N 个整数,第i 个表示在第i 个城市所能拍出的照片质量Ci。 
  接下来N – 1 行,每行有两个整数u,v             (1≤u,v≤N,u≠v),表示城市u 与城市v 之间有 
一条公路连接。 

输出格式输出格式 
输出格式输出格式 

  输出数据仅一行,要求输出照片的最高总质量。 

样例输入输出样例输入输出 
样例输入输出样例输入输出 

Sample #1 
photo.in                                photo.out 
5                                       4 
-1 1 3 1 -1 
4 1 
1 3 
1 2 
4 5 

Sample #2 
photo.in                               photo.out 
4                                       5 
-1 3 1 2 
4 1 
1 3 
1 2 

数据规模数据规模 
数据规模数据规模 

对于10%的数据,N≤10,|Ci|≤1000; 
对于30%的数据,N≤1000,|Ci|≤2000; 
对于50%的数据,N≤10000,|Ci|≤4000; 
对于100%的数据,N≤200000,|Ci|≤8000。 

时间限制时间限制 
时间限制时间限制 
1s 

这道题我一看就是树形DP,没觉得有多难,但是却只得了30分。

不究其根本的话,失分70分,完全不应该,至少可以得50分的。好一点可以AC。

损失的20分在于,标记变量完全不用赋初值,这是一个记忆化深搜模块,因此应该在回溯过程中修改标记变量。这一点可以多过20分。

损失的另外50分在于,没有卡节点,经后来实验发现,卡节点的范围其实很宽,基本上卡得准。


看见这道题,我有考虑使用多叉转二叉。

但是仔细考虑一下,这道题并没有愚蠢的矿工那样复杂。

只需要从各个子树中找一个最大和即可(这一点容易实现,因为如果不选取,则可设为0)

所以不需要使用多叉转二叉。


这道题我的方程是(程序中没有状态变量,因为我觉得貌似这种树形动规,不会有重复子问题)

f[i] = max{sigma(f[j])+c[i],sigma(f[i]),f[i]}这个方程是正确的。

但是一开始犯了和汪正正一样的错误,就是这个方程表示的是i一定要去的最优解。

一开始随便指定了一个根((n+1)/2,因为担心评测机卡以1为根)

交卷前10分钟发现错误,就是不要这个根也许更优。

没有想出更好的方法,于是采用了和汪正正一样的方法,就是对每个点作根进行DP。

我怀疑会超时,结果果然超时。。。和初中高中的同校的学长悲剧到一起了。我没有用卡节点,汪维正卡错了。

(听他说这个状态设计只能采取卡节点的方式AC)

经过尝试。最终发现问题是出在没有沿途记录最优解,因为f[i]表示以i为根的最优解,因此只要沿途更新ans值就好了。

(例如,f[j]>f[i],则j更优,并且不用考虑j的父亲i了,因为如果把i当作j的子树重新建树,则得到的解和以i为根是相同的。

其实就是根i+除了)

改正之后正确。


教训有四个:

1、标记变量赋初值一定要考虑清楚。(一般来说dfs,spfa,只要符合逻辑,尽量考虑回溯或出队时赋值)

2、感觉要超时时果断卡节点(如果本身不会超时,卡节点一般也不会导致出错,只要卡准点)

3、这个是好的教训,就是自从前两天考试那两道悲剧地手残了的题中学习到的。

建树并不一定要明确地建,可以在搜索过程中,通过标记变量来动态地建树。

还有一点,如果本来就是一棵树,只是边的方向不确定,建出来的树形状一定是唯一的。(只是根与叶的关系变了)

这个容易证明,反证法,如果形状变了的话,一定会出现环和孤立的点,这与一般题设中的“两个点之间有且仅有一条路径”矛盾“

今天我算是终于领悟了这一建树的精髓了。

4、树形动规也需要记忆化,并不是没有重复子问题。。但这是为什么呢??反正以后都记得要记忆化



总之是一道简单的树形动规,超时7组实在不划算。

我的方法

//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdlib>
#include <cstdio>
const long oo = 0x7fff0000;
struct tnode
{
	long index;
	tnode* next;
};
tnode* city[200002];
bool used[200002];
long n;long c[200002];

long tim = 0;

long max = -oo;

long dp(long l)
{
	tim ++;
	if (tim==480000)
	{
		printf("%ld",max);
		exit(0);
	}
	long value = 0;
	tnode* ths = city[l];

	while (ths)
	{
		if (used[ths->index])
		{
			ths = ths->next;
			continue;
		}
		used[ths->index] = true;
		value += dp(ths->index);
		used[ths->index] = false;
		ths = ths->next;
	}
	if (value+c[l]>0)
		return value+c[l];
	else if(c[l]>0)
		return c[l];
	else
		return 0;
}

void insert(long u,long v)
{
	tnode* nn = new tnode;
	nn->index = v;
	nn->next = city[u];
	city[u] = nn;
}

int main()
{
	freopen("photo.in","r",stdin);
	freopen("photo.out","w",stdout);
	
	scanf("%ld",&n);
	for (long i=1;i<n+1;i++)
		scanf("%ld",c+i);
	for (long i=1;i<n;i++)
	{
		long u;long v;
		scanf("%ld%ld",&u,&v);
		insert(u,v);
		insert(v,u);
	}

	for (long i=1;i<n+1;i++)
	{
//		for (long j=1;j<n+1;j++)
//			used[j] = false;
		used[i] = true;
		max >?= dp(i);
	}
	
	printf("%ld",max);
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值