problem
给定一张图 nnn 个点 mmm 条边,并给定阈值 kkk,以及起终点 s,ts,ts,t。
然后每条边经过都需要支付 www 的花费,形如 (u,v,w)(u,v,w)(u,v,w) 格式给出。
求 s→ts\rightarrow ts→t 的最小花费。
最小花费定义如下:
- 如果该路径经过的边数 ≤k\le k≤k,则就是该路径的边权和。
- 如果该路径经过的边数 >k>k>k,则只需要求前 kkk 大的边权和。
n,k≤1000,m≤2000,1≤wi≤1e9n,k\le 1000,m\le 2000,1\le w_i\le 1e9n,k≤1000,m≤2000,1≤wi≤1e9。
原题:CF Gym 101630 J
solution
考试设计的部分分实际上能让纯暴力搜路径做法拿到 70 的高分?!!(○´・д・)ノ
我们直接枚举第 kkk 大的边权 xxx,然后把所有边权全都减去 xxx,当然需要和 000 比 max\text{max}max。
然后求 s→ts\rightarrow ts→t 的最短路 +x∗k+x*k+x∗k 就是最短路的真正花费,再全局取最小值即可。
这样子做,我们可以求出所有第 kkk 大边权是 xxx 的所有路径。
如果这条路径不足 kkk,那么你让 kkk 大边权为 000 就行了。
接下来我们要证明此时所有第 kkk 大边权 ≠x\neq x=x 的路径花费都不存在算得比真实花费小的情况。
只要不影响到最终答案即可。
证明其实很简单。可能不太严谨??
-
第 kkk 大边权 <x<x<x。
那么前 kkk 大至少有一个减掉后边权成为 000,后面用 k∗xk*xk∗x 抵消这个减去的边权时,相当于多弥补了一些花费。
-
第 kkk 大边权 >x>x>x。
那么除去前 kkk 大,后面的某些边权可能也会 >x>x>x,减去后还是存在花费会被统计进路径中。
而实际上后面这些边权都是不用算的。
那么这道题就变成了 O(n2logn)O(n^2\log n)O(n2logn) 了。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 1005
#define int long long
#define Pair pair < int, int >
int n, m, k, s, t;
struct edge{ int u, v, w; }E[maxn << 1];
priority_queue < Pair, vector < Pair >, greater < Pair > >q;
int dis[maxn];
vector < pair < int, int > > G[maxn];int dijkstra() {memset( dis, 0x3f, sizeof( dis ) );q.push( make_pair( dis[s] = 0, s ) );while( ! q.empty() ) {int u = q.top().second; int w = q.top().first; q.pop();if( dis[u] ^ w ) continue;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first; w = G[u][i].second;if( dis[v] > dis[u] + w ) q.push( make_pair( dis[v] = dis[u] + w, v ) );}}return dis[t];
}int solve( int x ) {for( int i = 1;i <= n;i ++ ) G[i].clear();for( int i = 1;i <= m;i ++ ) G[E[i].u].push_back( make_pair( E[i].v, max( 0ll, E[i].w - x ) ) ); return dijkstra() + x * k;
}signed main() {scanf( "%lld %lld %lld %lld %lld", &n, &m, &k, &s, &t );for( int i = 1;i <= m;i ++ ) scanf( "%lld %lld %lld", &E[i].u, &E[i].v, &E[i].w );int ans = solve( 0 );for( int i = 1;i <= m;i ++ ) ans = min( ans, solve( E[i].w ) );printf( "%lld\n", ans );return 0;
}