来源:Luogu P2966,JZOJ #318
题目描述
跟所有人一样,农夫约翰以着宁教我负天下牛,休叫天下牛负我的伟大精神,日日夜夜苦思生 财之道。为了发财,他设置了一系列的规章制度,使得任何一只奶牛在农场中的道路行走,都要向农夫约翰上交过路费。
农场中由 片草地(标号为 到 ),并且有 条双向道路连接草地 和 。奶牛们从任意一片草地出发可以抵达任意一片的草地。 已经在连接 和 的双向道路上设置一个过路费 。 可能有多条道路连接相同的两片草地,但是不存在一条道路连接一片草地和这片草地本身。最值得庆幸的是,奶牛从任意一篇草地出发,经过一系列的路径,总是可以抵达其它的任意一片草地。除了贪得无厌,叫兽都不知道该说什么好。
FJ竟然在每片草地上面也设置了一个过路费 。从一片草地到另外一片草地的费用,是经过的所有道路的过路费之和,加上经过的所有的草地(包括起点和终点)的过路费的最大值。
任劳任怨的牛们希望去调查一下她们应该选择那一条路径。她们要你写一个程序,接受 个问题并且输出每个询问对应的最小花费。第i个问题包含两个数字 和 ,表示起点和终点的草地。
考虑下面这个包含
片草地的样例图像:
从草地
到草地
的道路的“边过路费”为
,草地
的“点过路费”为
。
要从草地 走到草地 ,可以从草地 走到草地 再走到草地 最后抵达草地 。如果这么走的话, 需要的“边过路费”为 ,需要的点过路费为 (草地 的点过路费最大),所以总的花 费为 。
而从草地 到草地 的最佳路径是从草地 出发,抵达草地 ,最后到达草地 。这么走的话,边过路费为 ,点过路费为 ,总花费为 。
解题思路
- ,点很少,最暴力的方法就是 的,写起来简洁明了;
- 首先,我们按点权从小到大排序,有点权小的不拿白不拿嘛;
- 这里注意:有一个预处理,就是开一个结构体,记录点的编号 ,再另外开一个数组 , 表示原来编号为 的点排在第 个;,以便在排序后能方便快捷地找到排序前与排序后点的对应位置;
- 可是这个新奇的点权怎么处理呢?这着实是个问题。
- 在 中,因为 的枚举是从小到大的,所以 为此路径的最大点权
代码君
#include <bits/stdc++.h>
using namespace std;
const int MAXN=250+10,MAXM=10000*2+10;
int n,m,q;
int dis[MAXN][MAXN],Cos[MAXN][MAXN],rank[MAXM];
struct node
{
int x,pos;
}c[MAXN]={};
inline int read() //快读
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void write(int x) //快写
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9) write(x/10);
putchar(x%10+'0');
}
bool mycmp(node a,node b) //按点权排序
{
return a.x<b.x;
}
int main()
{
freopen("toll.in","r",stdin);
freopen("toll.out","w",stdout);
n=read(); m=read(); q=read();
for (int i=1;i<=n;i++)
{
c[i].x=read();
c[i].pos=i; //记录点编号
}
sort(c+1,c+n+1,mycmp);
for (int i=1;i<=n;i++)
{
rank[c[i].pos]=i; //原来编号为c[i].pos的点排在第rank[c[i].pos]个
}
memset(dis,10,sizeof(dis)); //初值:正无穷
memset(Cos,10,sizeof(Cos));
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),v=read();
dis[rank[x]][rank[y]]=min(dis[rank[x]][rank[y]],v); //邻接矩阵
dis[rank[y]][rank[x]]=min(dis[rank[x]][rank[y]],v);
}
for (int i=1;i<=n;i++) dis[i][i]=0;
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (i!=j)
{
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //最短路
Cos[i][j]=min(Cos[i][j],dis[i][j]+max(c[i].x,max(c[k].x,c[j].x))); //加上点权的最短路
}
}
for (int i=1;i<=q;i++) //q次查询
{
int st,en;
st=read(); en=read();
write(Cos[rank[st]][rank[en]]); //输出答案
printf("\n");
}
return 0;
}