个人笔记,仅供复习
上学
【题意描述】
小喵喵家附近有n个路口,m条道路连接着这些路口,而且通过这些道路各自需要一些时间c[i]。小喵喵在1号路口,他要去n号路口上学,但是他马上要迟到了,所以他想知道最短需要多长时间能够到达学校。其中忽略小喵喵由于出家门、到n号路口之后进入学校、从一条道路移动到另一条道路之类的时间,只计算他在这些道路上花费的时间总和。
除此之外,小喵喵还有k个时间暂停器,每个可以使某一条道路的经过时间变为0.
【输入格式】
第1行三个整数n,m,k,表示路口个数,道路的数量,时间暂停器的数量。
第2~m+1行每行三个整数u[i],v[i],c[i],表示u[i]和v[i]之间有一条可以双向通行的道路,通过所需时间为c[i]。
【输出格式】
一个整数,表示小喵喵所需的最短时间。
【样例输入输出】
5 6 1
1 2 2
1 3 4
2 4 3
3 4 1
3 5 6
4 5 2 3
【样例解释】
小喵喵选择从1->3->4->5,并且在1->3的道路上使用时间暂停器。
总耗时为0+1+2=3.
【数据范围】
对于20%的数据,n<=5,m<=10.
对于另外20%的数据,k=0.
对于另外30%的数据,k=1.
对于所有的数据,n<=1000,m<=10000,k<=10,c[i]<=109,保证存在从1号路口到n号路口的路径。注意,可能存在两条道路的起点和终点组成的集合相同,也可能存在起点和终点是相同路口的道路。
解题思路:如果没有c[i]的话,这题毫无疑问就是典型的图论中求最短路的问题,但是他可以任意选几段路使时间为0,这样的话就没办法用一层图来解决。我们可以考虑建立k层图,不同层间的路径权值为0,其中跨图走为单向,即一旦走到下层图,就不能再返回上层图,这样就保证了只使用k次时间暂停器。
解题代码:(来自老师)
#include <iostream>
#include <queue>
using namespace std;
struct Node{
int v, w;
Node(){}
Node(int av, int aw): v(av), w(aw){}
bool operator < (const Node& b) const {
return w > b.w;
}
}node;
vector<vector<Node> > g;
queue<Node> q;
vector<long long> dist, uptimes;
const long long INF = (long long)1 << 62;
int n, m, k, u, v, w;
bool spfa() {
q.push(Node(1+k*n, 0)); dist[1+k*n] = 0;
while (q.size()) {
node = q.front(), q.pop();
for (int i = 0, j = g[node.v].size(); i < j; ++i) {
Node tN = g[node.v][i];
if (dist[tN.v] > dist[node.v] + tN.w) {
dist[tN.v] = dist[node.v] + tN.w, q.push(Node(tN.v, node.w+tN.w));
if(++uptimes[tN.v] >= n) return false;
}
}
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m >> k;
g.resize(m*(k+2)); dist.resize(m*(k+2)); uptimes.resize(m*(k+2));
fill(dist.begin(), dist.end(), INF);
for (int i = 1; i <= m; ++i) {
cin >> u >> v >> w;
//使用多层图,将动态规划的各个状态以不同层列出,0边在不同层之间连接,从而避免了重边
for (int j = 0; j <= k; ++j) {
g[u+j*n].push_back(Node(v+j*n, w));
g[v+j*n].push_back(Node(u+j*n, w));
if (j) {
g[u+j*n].push_back(Node(v+(j-1)*n, 0));
g[v+j*n].push_back(Node(u+(j-1)*n, 0));
}
}
}
spfa();
long long minN = INF;
for (int i = 0; i <= k; ++i)
minN = min(minN, dist[n+i*n]);
cout << minN << endl;
return 0;
}