【最短路 动态规划】洛谷_1948 电话线Telephone Lines

题意

给出一个图,求出其中一条从点111到点NNN的路径,在上面选掉KKK条边,使得剩下的边中最大的最小。

思路

这个题其实是求出一条111NNN的路径中使得第K+1K+1K+1的边最小。

解法一:

首先看到字样“最大的最小”,我们可以感觉这个题可以用二分,然后细读一下发现答案满足单调性。
当我们二分出一个midmidmid,可以求出这个图中从111NNN的路径中大于midmidmid的边的个数,如果个数超过了KKK,说明答案不可行。
求出这个边的个数我们只用把超过midmidmid的边权记为111,然后利用SPFASPFASPFA或双端队列BFSBFSBFS的方法就可以求出了。

解法二:

可以用动态规划,设f[u][k]f[u][k]f[u][k]为从111uuu中已经选了kkk条路的最优答案,可得:
表示选f[v][k+1]=min(f[v][k+1],f[u][k])f[v][k+1]=min(f[v][k+1],f[u][k])f[v][k+1]=min(f[v][k+1],f[u][k])
表示不选f[v][k]=min(f[v][k],max(f[u][k],e.v)f[v][k]=min(f[v][k],max(f[u][k],e.v)f[v][k]=min(f[v][k],max(f[u][k],e.v)
其中vvv表示边的另一个点,e.ve.ve.v表示权值。
因为没有一个确定的顺序dpdpdp,我们可以用SPFASPFASPFA来转移,相当于取minminmin就为松弛操作。

代码

解法一:

#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

int N, P, K, tot, l, r;
int head[1001], vis[1001], d[1001];
struct node{
    int to, v, next;
}e[20001];

void add(int x, int y, int z) {
    e[++tot].to = y;
    e[tot].v = z;
    e[tot].next = head[x];
    head[x] = tot;
    e[++tot].to = x;
    e[tot].v = z;
    e[tot].next = head[y];
    head[y] = tot;
}

int check(int x) {
    deque<int> q;
    memset(d, 0, sizeof(d));
    memset(vis, 0, sizeof(vis));
    q.push_back(1);
    vis[1] = 1;
    while (q.size()) {
        int u = q.front(), v;
        q.pop_front();
        for (int i = head[u]; i; i = e[i].next) {
            v = e[i].to;
            if (!vis[v] || d[v] >= d[u] + 1) {
                vis[v] = 1;
                //双端队列做法可保证每次取出来的点都是最优的
                if (e[i].v > x) q.push_back(v), d[v] = d[u] + 1;
                else q.push_front(v), d[v] = d[u];
            }
        }
    }
    return d[N] <= K && vis[N];
}

int main() {
    scanf("%d %d %d", &N, &P, &K);
    for (int i = 1, x, y, z; i <= P; i++) {
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
        r = max(r, z);
    }
    l = 1;
    int mid, f = 0;//f记录无解情况
    while (l < r) {
    	mid = (l + r) >> 1;
    	if (check(mid)) r = mid, f = 1;
    	else l = mid + 1;
    }
    printf("%d", f ? r : -1);
}

解法二:

#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

int N, P, K, tot;
int head[1001], f[1001][1001], v[1001][1001];
struct node{
    int to, v, next;
}e[20001];

void add(int x, int y, int z) {
    e[++tot].to = y;
    e[tot].v = z;
    e[tot].next = head[x];
    head[x] = tot;
    e[++tot].to = x;
    e[tot].v = z;
    e[tot].next = head[y];
    head[y] = tot;
}

void spfa() {
    memset(f, 127 / 3, sizeof(f));
    queue< pair<int, int> > q;
    q.push(make_pair(1, 0));
    v[1][0] = 1;
    f[1][0] = 0;
    pair<int, int> a;
    while (q.size()) {
        a = q.front();
        q.pop();
        v[a.first][a.second] = 0;
        for (int i = head[a.first]; i; i = e[i].next) {
            if (a.second < K && f[a.first][a.second] < f[e[i].to][a.second + 1]) {//use
                f[e[i].to][a.second + 1] = f[a.first][a.second];
                if (!v[e[i].to][a.second + 1]) {
                    v[e[i].to][a.second + 1] = 1;
                    q.push(make_pair(e[i].to, a.second + 1));
                }
            }
            if (max(e[i].v, f[a.first][a.second]) < f[e[i].to][a.second]) {//not use
                f[e[i].to][a.second] = max(e[i].v, f[a.first][a.second]);
                if (!v[e[i].to][a.second]) {
                    v[e[i].to][a.second] = 1;
                    q.push(make_pair(e[i].to, a.second));
                }
            }
        }
    }
}

int main() {
    scanf("%d %d %d", &N, &P, &K);
    for (int i = 1, x, y, z; i <= P; i++) {
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
    }
    spfa();
    printf("%d", f[N][K] == 707406378 ? -1 : f[N][K]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值