2015.9.11模拟赛 codevs4162 bzoj1774【无双大王】

题目描述 Description

无双大王hzwer扫清六合,席卷八荒,万姓倾心,四方仰德。

hzwer拥有一片领土,其中有n个城市和m条双向道路。他规定每个人在领土上行走都要交过路费,同时进城也要交进城费。不同道路的过路费可能不同,不同城市的进城费可能不同。但是hzwer规定,如果缴纳x的进城费,那么所有小于x的进城费就不用缴纳了。(即只缴纳一条路径上的所有过路费和最大的进城费)那么从s城市出发到t城市,要缴纳多少费用?(s城市和t城市进城费也要算)

输入描述 Input Description

第一行两个数n,m,q。表示n个城市m条路q个询问。

接下来n个数,表示n个城市的进城费。

接下来m行,每行3个数,表示一条路径的两端和过路费。

接下来q行,每行两个数s,t,表示询问从s到t的最小花费。

输出描述 Output Description

对于每个询问,输出一行表示最小花费。

样例输入 Sample Input

5 7 2
2 5 3 3 4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 4
2 3

样例输出 Sample Output

8

9

数据范围及提示 Data Size & Hint

对于20%数据,1<=n<=10.

对于60%数据,1<=n<=100.

对于100%数据,1<=n<=250,1<=m,q<=10000

 

题意是给定一个只有双向边的连通图,点有点权,边有边权,定义一条路径的费用是这条路径上的边权和加路径上的点权的最大值,询问两点之间的最小费用

看到n=250不难想到floyd.但是很容易发现,由于点权的影响,单纯地cost[i,k]+cost[k,j]到cost[i,j]的转移是不行的。

假设从点1到点2有两条路,一条dist=1,maxv=7.另一条dist=4,maxv=5.则第一条费用是8,第二条费用是9,我们会选择第1条。

但是假设从点2到点3有一条路,dist=3,maxv=4,容易看出一开始选第一条路费用是1+7+3=13,选第二条路费用是4+5+3=12.所以第2条会更优。

如果用个max[i,j]存i到j的最小费用路径上的点权最大值再想办法转移之类的也是不行的,因为不能转移的本质原因是不知道最大点权的点的位置,而求最短路又和路径有关。因此没办法判断最大点是否在某一段路径上,也就不能转移。除非再开一维保存最大点权的位置,才可以转移。那样复杂度就到n^4以上了。

回归到floyd的原型,其实就是个dp。f[i][j][k]表示从i到j的路径上除了两端点只经过编号1到k的点时的距离最小值。那么f[i][j][k]=min(f[i][j][k-1],f[i][k][k-1]+f[k][j][k-1]).最后一维因为总是从上一状态转移而来所以可以省略。

那么考虑floyd的枚举顺序,当我们枚举k,i,j时,经过的点只有i,j,还有编号1到k的k个点。如果按照点权从小到大排个序,那么编号为k的点比其他1到k中的点的点权都大。

所以只要点权考虑v[i],v[j],v[k]的最大值即可,而边权同floyd。

ps:"如果是一棵树的话,闲的没事还可以用link cut tree来做"——by hzwer

conclusion:一开始看到这道题的时候,我也没有从这方面去想。题目没有什么外延想法或者结论,只利用了floyd的性质,并从这一点出发,应该说整个思路都是很自然的。但是我们在背模板的时候,在为10分钟打出了无脑的线段树平衡树树套树而沾沾自喜的时候,是否注意到了这些算法背后的东西?如果没有积累一点深刻的认识,恐怕再简单不过的东西都能把你考倒。

 #include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
#include<set>
#include<map>
#include<ctime>
#define LL long long
#define inf 0x7fffffff
#define pa pair<int,int>
#define pi 3.1415926535897932384626433832795028841971
#define N 1010
using namespace std;
inline LL read()
{
LL x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
struct srt{int x,rnk;}cdk[];
bool operator <(srt a,srt b){return a.x<b.x;}
int dis[][];
int ans[][];
int v[];
int n,m,q;
int main()
{
memset(dis,/,sizeof(dis));
memset(ans,/,sizeof(ans));
for (int i=;i<=n;i++)dis[i][i]=;
n=read();m=read();q=read();
for (int i=;i<=n;i++)
{
cdk[i].rnk=i;
cdk[i].x=read();
v[i]=cdk[i].x;
}
sort(cdk+,cdk+n+);
for (int i=;i<=m;i++)
{
int x=read(),y=read(),z=read();
dis[x][y]=min(dis[x][y],z);
dis[y][x]=min(dis[y][x],z);
}
for (int kk=;kk<=n;kk++)
{
int k=cdk[kk].rnk;
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
if (k!=i&&k!=j&&i!=j)
{
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
ans[i][j]=min(ans[i][j],dis[i][j]+max(max(v[i],v[j]),v[k]));
}
}
for (int i=;i<=q;i++)
{
int x=read(),y=read();
printf("%d\n",ans[x][y]);
}
}

codevs4162 && bzoj1774

上一篇:Javascript学习7 - 脚本化浏览器窗口


下一篇:有n个台阶,如果一次只能上1个或2个台阶,求一共有多少种上法