题目链接
http://codeforces.com/gym/101630/attachments/download/6401/20172018-acmicpc-northeastern-european-regional-contest-neerc-17-en.pdf
题意
求11到的最短路,最短路上只计算前kk大的边。
题解
这道题的操作很骚,算法如下:
遍历每条边,并把图中所有的边的权值都减去该边的权值xx,如果变成负数,那么就置,并将跑出来的值dis[n]+k∗xdis[n]+k∗x就是这次的答案,对所有的答案取最小值,并且与原始图的dis[n]dis[n]取最小值,得到的结果就是最终答案。
正确性证明:
1. 假设最终的最短路中有大于等于kk条边,并设第大的边长度为xx。
那么该路径上所有的边减去,之后跑最短路得到dis[n]dis[n],那么dis[n]+k∗xdis[n]+k∗x就是该路径的“长度”,就是最终答案,并且这个数一定会出现在比较中。
而对于其他的路径,如果减去的xx不是该路径的第大的边的时候,该路径的值dis[n]+k∗xdis[n]+k∗x一定不会比该路径的“长度”小,出现在比较中不会对最终答案造成影响。
2. 假设最短路中有小于k条边,那么原图中跑最短路的dis[n]dis[n]一定是最小的。
注意,此题用spfa超时,用dijkstra也有稍微优化一下才行。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 3007;
vector<int> G[maxn];
typedef long long ll;
typedef pair<ll,int> pii;
const ll inf = 1e18;
int U[maxn],V[maxn];long long W[maxn];
int n,m,k;
int vis[maxn];ll dis[maxn];
#define pr(x) cout<<#x<<":"<<x<<endl
/*
ll spfa(ll x){memset(vis,0,sizeof(vis));for(int i = 1;i <= n;++i) dis[i] = inf;queue<int> Q;vis[1] = 1;Q.push(1);dis[1] = 0;while(!Q.empty()){int u = Q.front();Q.pop();vis[u] = 0;for(auto e : G[u]){int v = U[e]^V[e]^u;ll w = W[e] - x;w = max(0ll,w);if(dis[v] > dis[u] + w){if(!vis[v]) Q.push(v);dis[v] = dis[u] + w;vis[v] = 1;}}}return dis[n];
}
*/
ll dij(ll x){priority_queue<pii,vector<pii>,greater<pii> > Q;for(int i = 1;i <= n;++i) dis[i] = inf;dis[1] = 0;Q.push({0,1});while(!Q.empty()){pii p = Q.top();Q.pop();int u = p.second;if(p.first > dis[u]) continue;for(int e : G[u]){int v = u ^ U[e] ^ V[e];ll w = W[e] - x;w = max(0ll,w);if(dis[v] > dis[u] + w){dis[v] = dis[u] + w;Q.push({dis[v],v});}}}return dis[n];
}
int main(){cin>>n>>m>>k;for(int i = 0;i < m;++i){int u,v;long long w;scanf("%d %d %lld",&u,&v,&w);U[i] = u,V[i] = v,W[i] = w;G[u].push_back(i);G[v].push_back(i);}ll ans = dij(0);for(int i = 0;i < m;++i){ll res = dij(W[i])+k*W[i];ans = min(ans,res);}printf("%lld\n",ans);return 0;
}