Gundam 0079

题目链接 http://acm.sdut.edu.cn/onlinejudge3/contests/4098/problems/M

简要题意:

给出一张 N个节点 M 条边的无向图,节点编号从 1到 N,每条边有一个正整数权重。

请你给出一条从 11 节点到 NN 节点的路径,使得边权值累加最小。此外你可以选择 K 条边,使其权重缩小为原来的 1/3​
(向下取整)

解析:

• 对于这种花里胡哨的最短路题目,只要想好怎么建图剩下的就非常简单了。
• 我们考虑建图的时候每个节点,并不只是图上的一个节点,而是一个状态,每条边是不同状态间的
转移。
• 对于本题,我们定义状态 disti,j 为从 1 点出发到达 i 点并还可以减少 j 条边的花费时的最小费用,
比如假设有一个 u 到 v,花费为 w 的边,我们可以建 2K − 1 条边:
通过优先队列来决定接下来的方向,每一条边决定是不是要使用加速(通过比较耗费)。u是起始点,v是目标点。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAX = 112;
const int INF = 0x3f3f3f3f;

vector<pair<int, int> > edge[MAX];
int dist[MAX][MAX];
int vis[MAX][MAX];
int N, M, K;

void init() {
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < MAX; ++i) edge[i].clear();
}

void dijkstra() {
    memset(dist, INF, sizeof(dist));
    priority_queue<pair<int, pair<int, int> > > q;
    dist[1][K] = 0;
    vis[1][K] = 1;
    int sumx=0;
    q.push(make_pair(1, make_pair(1, K)));//初始是k次,
    while (!q.empty()) {
        sumx++;
        int u = q.top().second.first;
        cout<<sumx<<" :"<<q.top().first<<endl;
        int k = q.top().second.second;
        q.pop();

        for (int i = 0; i < edge[u].size(); i++) {
            int v = edge[u][i].first;
            int w = edge[u][i].second;
            if (dist[v][k] > dist[u][k] + w) {
                dist[v][k] = dist[u][k] + w;
                if (!vis[v][k]) {
                    q.push(make_pair(-dist[v][k], make_pair(v, k)));
                }
                vis[v][k] = 1;
            }
            if (k > 0) {
                w /= 3;
                if (dist[v][k - 1] > dist[u][k] + w) {
                    dist[v][k - 1] = dist[u][k] + w;
                    if (!vis[v][k - 1]) {
                        q.push(make_pair(-dist[v][k - 1], make_pair(v, k - 1)));
                    }
                    vis[v][k - 1] = 1;
                }
            }
        }
        vis[u][k] = 0;
    }
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        init();
        cin >> N >> M >> K;
        for (int i = 0; i < M; ++i) {
            int u, v, w;
            cin >> u >> v >> w;
            edge[u].emplace_back(v, w);
            edge[v].emplace_back(u, w);
        }
        dijkstra();

        int ans = INF;
        for (int i = 0; i <= K; ++i) {
            ans = min(ans, dist[N][i]);
        }

        if(ans == INF) {
            cout << -1 <<endl;
        } else {
            cout << ans << endl;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值