问题描述
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走 s公里小明会增加 s 2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2) 2+2+2 2=16+2+4=22。
现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走 s公里小明会增加 s 2的疲劳度。
例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2) 2+2+2 2=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号路口是连通的。
接下来 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
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
样例说明
从1走小道到2,再走小道到3,疲劳度为5
2=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
对于30%的评测用例,1 ≤
n ≤ 8,1 ≤
m ≤ 10;
对于另外20%的评测用例,不存在小道;
对于另外20%的评测用例,所有的小道不相交;
对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 10 5,1 ≤ a, b ≤ n, t是0或1, c ≤ 10 5。保证答案不超过10 6。
对于另外20%的评测用例,不存在小道;
对于另外20%的评测用例,所有的小道不相交;
对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 10 5,1 ≤ a, b ≤ n, t是0或1, c ≤ 10 5。保证答案不超过10 6。
最开始的时候以为没看到疲劳值的递增规则,以为直接把小道的长度算平方就行了,结果白写,然后改了发现只有80分,上网看到说使用int类型会溢出。。。真的神奇。然后又改了一下 过了。
我的思路就是用一个pre来保存这个点的前驱点,以及前驱点到这个点的道路是小道还是大道。如果是小道,就一直往前累加,直到最开始的点或者前面的路是大道的点为止。然后从原点到要更新距离的点的距离就是原点到这个最终的前驱点的距离加从前驱点到要更新距离的点距离的平方。
叙述的很混乱,代码也很混乱,我加点注释吧,省得以后自己都看不懂。
加注释的时候发现自己的这个思路是有问题的。
如果从大道到某点距离比小道略长,但是某点向外全是小道,那么就有可能从大道到某点再过去更近,但是由于这个题用的是贪婪算法,所以会错。但是估计csp没考虑到这种情况,所以还是过了。
代码如下:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
long long n, m, t, a, b, c;
cin >> n >> m;
vector<vector<long long> > adj(n + 1, vector<long long>());//邻接表 我习惯用vector
for (long long i = 0; i<m; ++i)
{
cin >> t >> a >> b >> c;
adj[a].push_back(b);
adj[b].push_back(a);
adj[a].push_back(c);
adj[b].push_back(c);
adj[a].push_back(t);
adj[b].push_back(t); //每个点存的内容是三个信息 邻接点 距离 大道还是小道
}
vector<long long> dist(n + 1, 1000000); //原点到某点距离
vector<bool> inS(n + 1, false); //是否已经在S集中
vector<long long> temp;
temp.push_back(-1);
temp.push_back(0);
temp.push_back(0);
vector<vector<long long> > pre(n + 1,temp);
inS[1] = true;
dist[1] = 0;
pre[1][0]=1; //以上三行是对原点的修改
for (long long i = 0; i<adj[1].size(); i += 3)
{
if (adj[1][i+2]==0)
dist[adj[1][i]] = adj[1][i + 1];
else
dist[adj[1][i]] = adj[1][i + 1] * adj[1][i + 1];
pre[adj[1][i]][0] = 1;
pre[adj[1][i]][1] = adj[1][i + 1];
pre[adj[1][i]][2] = adj[1][i + 2];
} //把原点的邻接点的距离和pre信息修改
long long source;
while (!inS[n])
{
long long mindis = 1000000;
for (long long i = 1; i <= n; ++i)
{
if (!inS[i] && dist[i]<mindis)
{
mindis = dist[i];
source = i;
}
} //寻找最近到原点最近点
inS[source] = true;
long long size = adj[source].size();
for (long long i = 0; i<size; i += 3)
{
if (!inS[adj[source][i]])
{
long long dist2i; //原点到待更新点距离
if (adj[source][i + 2] == 0) //分情况考虑 如果是大道 直接加就行了
{
dist2i = dist[source] + adj[source][i + 1];
}
else //小道 一直向前寻找 看连续小道的距离
{
long long prenode = source;
long long count = adj[source][i + 1];
while (true)
{
if (pre[prenode][0] != prenode&&pre[prenode][2] == 1)
{
count += pre[prenode][1];
prenode = pre[prenode][0];
}
else
break;
}
dist2i = dist[prenode] + count*count;
}
if (dist2i < dist[adj[source][i]]) //判断更新与否
{
dist[adj[source][i]] = dist2i;
pre[adj[source][i]][0] = source;
pre[adj[source][i]][1] = adj[source][i + 1];
pre[adj[source][i]][2] = adj[source][i + 2];
}
}
}
}
cout << dist[n];
return 0;
}