蓝桥杯2013年第四届真题 大臣的旅费

蓝桥杯2013年第四届真题 大臣的旅费

题意

很久以前,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千米。

n的规模在1e5

输出

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

解题思路

其实这个题意就是想让我们求一个树的直径,求出输的直径来,最后在按照题目中说的所需金额的定义,其实公式就是$$s*10+(1+s)/2$$,其中s表示这个树直径的长度。

我开始的做法是使用了最短路来解决的,先任意选一个点,比如是1,跑完最短路就可以知道距离1号城市的最远城市的编号t,然后下次就以这个t为出发点,再跑一遍最短路,那么距离t最远的距离就是我们需要距离了。但是这样有个问题,最短路算法的选择问题。

因为n的规模在1e5,如果选择使用Dijkstra算法,那么使用邻接矩阵是超存储空间的,这个我也试了,只能开到1e4,因而得分不能得到全分,仅仅80分。但是使用优先队列改进的Dijkstra算法是可以的。

下面给出代码。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double eps=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=1E5+7;
int head[MAXN], cnt = 1;
struct stut{
	int to, w, next;
}edge[MAXN];
inline void addedge(int u, int v, int w){
	edge[cnt].to = v; edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}

struct Node{
	int u, d;
	friend bool operator < (const Node a, const Node b){
		return a.d > b.d;
	} 
	Node(int a, int b){
		u = a; d = b;
	}
}; 
priority_queue<Node> que;
int dis[MAXN];
bool vis[MAXN];
int n;
void dij(int s){
	for(int i=1; i<=n; i++){
		dis[i] = inf;
		vis[i] = false;
	}
	dis[s] = 0;
	vis[s] = true;
	Node tmp(s, 0);
	que.push(tmp);
	int u;
	while(!que.empty()){
		u = que.top().u;
		que.pop();
		vis[u] = true;
		for(int i=head[u]; i>0; i=edge[i].next){
			if(!vis[edge[i].to] && dis[edge[i].to]>dis[u]+edge[i].w){
				dis[edge[i].to] = dis[u]+edge[i].w;
				tmp.d=dis[edge[i].to];
				tmp.u=edge[i].to;
				que.push(tmp);
			}
		}
	}
}
int main()
{
	scanf("%d", &n);
	int u, v, w;
	for(int i=1; i<n; i++){
		scanf("%d%d%d", &u, &v, &w);
		addedge(u, v, w);
		addedge(v, u, w);
	}
	dij(1);
	int s1, tmp = dis[1];
	for(int i=1; i<=n; i++)
		if(dis[i]>tmp){
			tmp = dis[i];
			s1 = i;
		}
	dij(s1);
	tmp = dis[1];
	for(int i=1; i<=n; i++){
		if(tmp<dis[i])
			tmp = dis[i];
	}
	ll res = tmp;
	printf("%lld\n", res*10+(res+1)*res/2);
	return 0;
}

虽然可以使用Dijstra算法来解决这个问题,但是我忽略了一个问题,就是这是一个棵树,树是没有环等等复杂结构的,因此是可以通过深搜加上dp来进行解决。

参考的这个博客:https://www.cnblogs.com/youdiankun/p/3591722.html

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double eps=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=1E5+7;
struct node{
	int to, w, nt;
}edge[MAXN];
int head[MAXN], m=1; //边是从1开始的,这样就不用初始化head了 
inline void addedge(int u, int v, int w){
	edge[m].to = v;
	edge[m].w = w;
	edge[m].nt = head[u];
	head[u] = m++;
} 
int dp[MAXN];
bool vis[MAXN];
int n, ans;
void dfs(int s){
	int k = head[s];
	int tmp = 0;
	while(k>0){
		int t = edge[k].to;
		if(!vis[t]){
			vis[t] = 1;
			dfs(t);
			tmp = max(tmp, dp[s]+dp[t]+edge[k].w);
			dp[s]=max(dp[s], dp[t]+edge[k].w);
		}
		k = edge[k].nt;
	}
	ans = max(ans, tmp);
}
int main()
{
	scanf("%d", &n);
	int u, v, w;
	for(int i=1; i<n; i++){
		scanf("%d%d%d", &u, &v, &w);
		addedge(u, v, w);
		addedge(v, u, w);
	}
	vis[1] = true;
	dfs(1);
	ll ret = ans;
	printf("%lld\n", ret*(21+ret)/2);
	return 0;
}
posted @ 2021-03-12 22:00  ALKING1001  阅读(194)  评论(0编辑  收藏  举报