蓝桥杯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来进行解决。
#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;
}