NEERC2017 J - Journey from Petersburg to Moscow

NEERC2017 J - Journey from Petersburg to Moscow

传送门

大概是开学了发现自己要补的东西是在是太多临时想搞一点掉

题意:有一个n点m边有边权的无向图,从一个点走到另一个点的路上可以只花费最贵的k条边的边权的代价,不足k则花费所有边权和的代价,问从1到n的最短路是多少

  1. 假设这条路长度不足k,答案包含在从1到n的最短路中

  2. 长度超过k,假设第k长的边的边权为x,将图中所有边权替换成 m a x { 0 , w − x } max\{0, w- x\} max{0wx},最终答案为替换后图从1到n的最短路d 加上 k × x k\times x k×x

  3. 假设要求的这条路长度为l,中间经过的所有边长排序后为: c 1 ≥ c 2 ≥ c 3 ≥ ⋯ ≥ c l c_1\ge c_2\ge c_3 \ge \dots \ge c_l c1c2c3cl

    c i > x ≥ c i + 1 c_{i } > x \ge c_{i+1} ci>xci+1 ,考虑 d + k × x d+k \times x d+k×x这个式子

    1. i ≥ k + 1 i \ge k +1 ik+1时, 当x增加1的时候,变化量是 − i + k -i+k i+k i ≥ k + 1 i \ge k +1 ik+1 , 单降

    2. i = = k i == k i==k 时,当x增加1的时候,变化量是 − i + k -i+k i+k = 0, 不变

    3. i < k i < k i<k 时,当x增加1的时候,变化量 − i + k -i+k i+k ,i < k 所以单增

      综上这个式子随着x越来越接近正确的取值先递减后递增,在真实取值附近取到最小值,即所求的答案

  4. 综上从0开始枚举x,每次将整个图的边替换为 m a x { 0 , w − x } max\{0, w- x\} max{0wx}, 然后求最短路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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值