NEERC2017 J - Journey from Petersburg to Moscow
大概是开学了发现自己要补的东西是在是太多临时想搞一点掉
题意:有一个n点m边有边权的无向图,从一个点走到另一个点的路上可以只花费最贵的k条边的边权的代价,不足k则花费所有边权和的代价,问从1到n的最短路是多少
-
假设这条路长度不足k,答案包含在从1到n的最短路中
-
长度超过k,假设第k长的边的边权为x,将图中所有边权替换成 m a x { 0 , w − x } max\{0, w- x\} max{0,w−x},最终答案为替换后图从1到n的最短路d 加上 k × x k\times x k×x
-
假设要求的这条路长度为l,中间经过的所有边长排序后为: c 1 ≥ c 2 ≥ c 3 ≥ ⋯ ≥ c l c_1\ge c_2\ge c_3 \ge \dots \ge c_l c1≥c2≥c3≥⋯≥cl,
c i > x ≥ c i + 1 c_{i } > x \ge c_{i+1} ci>x≥ci+1 ,考虑 d + k × x d+k \times x d+k×x这个式子
-
) i ≥ k + 1 i \ge k +1 i≥k+1时, 当x增加1的时候,变化量是 − i + k -i+k −i+k, i ≥ k + 1 i \ge k +1 i≥k+1 , 单降
-
) i = = k i == k i==k 时,当x增加1的时候,变化量是 − i + k -i+k −i+k = 0, 不变
-
) i < k i < k i<k 时,当x增加1的时候,变化量 − i + k -i+k −i+k ,i < k 所以单增
综上这个式子随着x越来越接近正确的取值先递减后递增,在真实取值附近取到最小值,即所求的答案
-
-
综上从0开始枚举x,每次将整个图的边替换为 m a x { 0 , w − x } max\{0, w- x\} max{0,w−x}, 然后求最短路d,用 d + k × x d + k\times x d+k×x 更新答案
大常数选手的参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const ll N = 3010;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct node {
ll to, len;
};
struct edge {
ll from, to, len;
};
edge edges[N];
vector<node> g[N];
ll dist[N];
ll n, m, k;
void init() {
for (ll i = 0; i <= n; ++i) {
dist[i] = inf;
g[i].clear();
}
}
void dijkstra(ll s) {
// memset(dist, 0x3f, sizeof dist);
priority_queue<pii, vector<pii>, greater<pii> > que;
dist[s] = 0;
que.push({0, s});
while (!que.empty()) {
auto tmp = que.top();
que.pop();
ll u = tmp.second;
if (tmp.first > dist[u]) continue;
ll siz = g[u].size();
for (ll i = 0; i < siz; ++i) {
ll to = g[u][i].to;
ll len = g[u][i].len;
if (dist[to] > dist[u] + len) {
dist[to] = dist[u] + len;
que.push({dist[to], to});
}
}
}
}
vector<ll> vec;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll mx = 0;
cin >> n >> m >> k;
for (ll i = 1; i <= m; ++i) {
cin >> edges[i].from >> edges[i].to >> edges[i].len;
mx = max(mx, edges[i].len);
vec.push_back(edges[i].len);
vec.push_back(edges[i].len - 1);
// vec.push_back(edges[i].len + 1);
}
vec.push_back(0);
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
ll ans = inf;
for(ll x : vec) {
init();
for(ll i = 1; i <= m; ++ i) {
ll from = edges[i].from;
ll to = edges[i].to;
ll len = edges[i].len;
len = max(0ll, len - x);
g[from].push_back({to, len});
g[to].push_back({from, len});
}
dijkstra(1);
ans = min(ans, dist[n] + k * x);
}
cout << ans << endl;
return 0;
}