蓝桥杯历届试题 大臣的旅费 记忆化搜索DFS

19 篇文章 0 订阅
9 篇文章 0 订阅

历届试题 大臣的旅费  
时间限制:1.0s   内存限制:256.0MB
       
问题描述

很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。

J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数

城市从1开始依次编号,1号城市为首都。

接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)

每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。

输出格式

输出一个整数,表示大臣J最多花费的路费是多少。

样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式

大臣J从城市4到城市5要花费135的路费。



因为地图是一棵树,所以可以知道最终最长的路是以叶子为起点的,而且树中点到点的路径是唯一的。直接DFS超时,不给数据范围也是醉了。

所以记忆化搜索来剪枝以减少搜索的复杂度。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;

const int maxn = 100000;

struct Edge {
	int u, v, w;
	Edge() {}
	Edge(int u, int v, int w) : u(u), v(v), w(w) {}
};

struct Rem {
	int nextt, maxx;
	Rem() {}
	Rem(int nextt, int maxx) : nextt(nextt), maxx(maxx) {}
};

int n, maxx;
vector<int> G[maxn + 5], leaf;
vector<Edge> edges;
bool vis[maxn + 5];
map<int, Rem> m;  //int对应当前点,Rem.nextt为当前点出发最长路路径中下一点
                  //Rem.maxx为从当前点出发的最长路长

int f(int x) {
	return x * (x + 1) / 2 + 10 * x;
}

//dfs返回从s出发的最长路长
int dfs(int s) {
	int tmax = 0, indexx = 1;
	bool flag = false;
	for (int i = 0; i < G[s].size(); i++) {
		Edge &e = edges[G[s][i]];
		int u = e.u, v = e.v, w = e.w;
		if (!vis[v]) {
			flag = true;
			if (m.find(v) != m.end() && m[v].nextt != u) {  //由于树中两点间路径是唯一的
				if (m[v].maxx + w > tmax) {                 //所以只要不回头,一定是路径下一点
					tmax = m[v].maxx + w;
					indexx = v;
				}
			}
			else {
				vis[v] = true;
				int t = dfs(v) + w;
				if (tmax < t) {
					indexx = v;
					tmax = t;
				}
				vis[v] = false;
			}
		}
	}
	if (flag) {
		m[s] = Rem(indexx, tmax);  //从s出发最长路长为tmax
		return tmax;               //且可通过下一步走s->indexx这条边达到此路长
	}
	else {
		return 0;  //搜索到头,返回0
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n - 1; i++) {
		int p, q, d;
		scanf("%d%d%d", &p, &q, &d);
		edges.push_back(Edge(p, q, d));
		G[p].push_back(edges.size() - 1);
		edges.push_back(Edge(q, p, d));
		G[q].push_back(edges.size() - 1);
	}

	for (int i = 1; i <= n; i++) {
		if (G[i].size() == 1) {  //叶子
			leaf.push_back(i);
		}
	}
	memset(vis, false, sizeof(vis));
	maxx = 0;
	m.clear();
	for (int i = 0; i < leaf.size(); i++) {
		int u = leaf[i];
		if (m.find(u) != m.end()) {  //之前记忆过了,直接用
			maxx = max(maxx, m[u].maxx);
		}
		else {
			vis[u] = true;
			maxx = max(maxx, dfs(u));
			vis[u] = false;
		}
	}
	printf("%d\n", f(maxx));
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值