行车路线(csp201712-4) :
问题描述
题目简述
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
输入/输出格式
输入格式:
输入的第一行包含两个整数n, m,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
接下来m行描述道路,每行包含四个整数t, a, b, c,表示一条类型为t,连接a与b两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
输出格式:
输出一个整数,表示最优路线下小明的疲劳度。
样例
输入样例:
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
输出样例:
76
问题分析
解题思路
差不多能看出这是一个dijkstra算法的题目。但是这个题在边权上做了一些文章,就是小路如果在相连的情况下,其所累计的疲劳值是和前面的小路的路程有关的。这样,直接使用dijkstra算法就没法表示这种情况了。因此,可以额外记录一个每个点之前连续走过的小路长度。这样,一旦从该点出发走小路,其增加的疲劳值就可计算了。总体直接跑dijkstra即可完成。
参考代码
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
const long long inf=30000000005;
class edge
{
public:
int index;
int from;
int to;
int weight;
edge(int lx,int u,int v,int w)
{
index=lx;
from=u;
to=v;
weight=w;
}
};
class heapnode
{
public:
int index;
long long weight;
heapnode(int id,long long w)
{
index=id;
weight=w;
}
bool operator <(const heapnode& hn) const
{
return weight>hn.weight;
}
};
vector<edge> edges;
vector<int> points[510];
priority_queue<heapnode> h;
int vis[510];
long long pre_length[510];
long long path_length[510];
int n,m;
void init()
{
edges.clear();
for(int i=0;i<510;i++)
{
points[i].clear();
path_length[i]=inf;
}
while(!h.empty()) h.pop();
memset(vis,0,sizeof(vis));
memset(pre_length,0,sizeof(pre_length));
}
void addedge(int lx,int u,int v,int w)
{
edge e1(lx,u,v,w);
edge e2(lx,v,u,w);
edges.push_back(e1);
points[u].push_back(edges.size()-1);
edges.push_back(e2);
points[v].push_back(edges.size()-1);
}
long long dijkstra()
{
heapnode np(1,0);
h.push(np);
pre_length[1]=0;
path_length[1]=0;
while(!h.empty())
{
heapnode tp=h.top();
h.pop();
int p_index=tp.index;
if(vis[p_index]==1) continue;
vis[p_index]=1;
if(p_index==n) return path_length[n];
for(int i=0;i<points[p_index].size();i++)
{
edge te=edges[points[p_index][i]];
long long t_path;
if(te.index==0)
{
t_path=te.weight+path_length[p_index];
}
else
{
t_path=path_length[p_index]-(pre_length[p_index]*pre_length[p_index])+(pre_length[p_index]+te.weight)*(pre_length[p_index]+te.weight);
}
if(t_path<path_length[te.to])
{
path_length[te.to]=t_path;
if(te.index==0) pre_length[te.to]=0;
else pre_length[te.to]=pre_length[p_index]+te.weight;
heapnode ad(te.to,path_length[te.to]);
h.push(ad);
}
}
}
}
int main()
{
init();
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
addedge(a,b,c,d);
}
printf("%lld",dijkstra());
return 0;
}
心得体会
写完之后我发现,其实还是有一些问题的,在一些特殊的情况下很可能出错:就比如,下面这个图:
3 3
1 1 2 1
0 1 2 1
1 2 3 1
就是说,1和2之间是由一条小路和一条大路连接的,,2和3之间是只有一条小路。此时,因为权重为1,从1出发走到2无论走大路还是小路疲劳值都是1,但是如果算法中认为走小路为最优,那么从2走到3的疲劳值最终为4,而不为理论值2。但是好像数据点没有这种情况。。但是为了准确,应该在dijkstra的松弛时额外判断相等的情况,在这种情况下,优先走大路,即可避免,修改后的djikstra算法如下:
long long dijkstra()
{
heapnode np(1,0);
h.push(np);
pre_length[1]=0;
path_length[1]=0;
while(!h.empty())
{
heapnode tp=h.top();
h.pop();
int p_index=tp.index;
if(vis[p_index]==1) continue;
vis[p_index]=1;
if(p_index==n) return path_length[n];
for(int i=0;i<points[p_index].size();i++)
{
edge te=edges[points[p_index][i]];
long long t_path;
if(te.index==0)
{
t_path=te.weight+path_length[p_index];
}
else
{
t_path=path_length[p_index]-(pre_length[p_index]*pre_length[p_index])+(pre_length[p_index]+te.weight)*(pre_length[p_index]+te.weight);
}
if(t_path<path_length[te.to])
{
path_length[te.to]=t_path;
if(te.index==0) pre_length[te.to]=0;
else pre_length[te.to]=pre_length[p_index]+te.weight;
heapnode ad(te.to,path_length[te.to]);
h.push(ad);
}
else if(t_path==path_length[te.to])
{
if(te.index==0) pre_length[te.to]=0;
}
}
}
}
经检验也通过了测试点。