架设电话线 题解

题目描述

原题来自:USACO 2008 Jan. Silver

在郊区有 N N N 座通信基站, P P P 条双向电缆,第 i i i 条电缆连接基站 A i A_i Ai B i B_i Bi。特别地, 1 1 1 号基站是通信公司的总站, N N N 号基站位于一座农场中。现在,农场主希望对通信线路进行升级,其中升级第 i i i 条电缆需要花费 L i L_i Li

电话公司正在举行优惠活动。农场主可以指定一条从 1 1 1 号基站到 N N N 号基站的路径,并指定路径上不超过 K K K 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。

一句话题意,在加权无向图上求出一条从 1 1 1 号结点到 N N N 号结点的路径,使路径上第 K + 1 K + 1 K+1 大的边权尽量小。

输入格式

第一行三个整数 N , P , K N, P, K N,P,K.

接下来 P P P 行,每行三个整数 A i , B i , L i A_i, B_i, L_i Ai,Bi,Li.

输出格式

若不存在从 1 1 1 N N N 的路径,输出 -1。否则输出所需最小费用。

样例输入

5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6

样例输出

4

分析

看到最大的最小?那一定是二分。看到第 K K K 大的最小?显然这还是二分。

我们可以直接二分答案。然后每次 c h e c k check check 答案的时候,将所有的大于这个值的边的权值改为 1 1 1,其余的改为 0 0 0。其实就是标记了一下比当前枚举答案大的。

然后跑最短路,如果最短路跑出来小于 K K K 则说明满足一条路径上二分的这个答案是第 K + 1 K + 1 K+1 大的,反之则说明当前枚举的答案不是可行解。继续二分。

接下来我们看看详细复杂,啰嗦,累赘的代码。

AC代码

#include 
#include 
#include 
#include 
using namespace std;

int n, p, k;
struct edge {  // 结构体 边
    int v, cost;
    edge() {}
    edge(int V, int Cost) {
        v = V;
        cost = Cost;
    }
};
const int MAXN = 2005;
const int INF = 0x3f3f3f3f;
vector<edge> graph[MAXN];
int ma = 0;
bool flag = true;
int dist[MAXN];
bool vis[MAXN];
vector<edge> mp[MAXN];

void read(int &x) {  // 读优
    int k = 1;
    x = 0;
    char s = getchar();
    while (s < '0' || s > '9') {
        if (s == '-')
            k = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 1) + (x << 3) + (s - '0');
        s = getchar();
    }
    x *= k;
    return;
}

void Add_Edge(int u, int v, int cost) {  // 存边
    graph[u].push_back(edge(v, cost));
    graph[v].push_back(edge(u, cost));
}

void init(int x) {  // 初始化
    for (int i = 1; i <= n; i++) mp[i].clear();
    memset(dist, 0x3f, sizeof dist);
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < graph[i].size(); j++) {
            int V = graph[i][j].v;
            int Cost = graph[i][j].cost;
            if (Cost > x) { // 跑最短路时的"01"图
                mp[i].push_back(edge(V, 1));
                mp[V].push_back(edge(i, 1));
            } else {
                mp[i].push_back(edge(V, 0));
                mp[V].push_back(edge(i, 0));
            }
        }
    //	for(int i = 1; i <= n; i++)
    //		for(int j = 0; j < mp[i].size(); j++)
    //			printf("%d %d %d\n", i, mp[i][j].v, mp[i][j].cost);
    return;
}

int dijkstra(int x) {  // dijkstra
    init(x);
    dist[1] = 0;
    for (int i = 1; i <= n; i++) {
        int k = 0, mi = INF;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && dist[j] < mi) {
                mi = dist[j];
                k = j;
            }
        vis[k] = true;
        for (int j = 0; j < mp[k].size(); j++) {
            int V = mp[k][j].v, Cost = mp[k][j].cost;
            if (dist[k] + Cost < dist[V])
                dist[V] = dist[k] + Cost;
        }
    }
    return dist[n];
}

bool check(int mid) {  // 二分 判断可行性函数
    int t = dijkstra(mid);
    if (t == INF) {
        flag = false;
        return false;
    }
    if (t > k)
        return false;
    return true;
}

int Find() {  // 二分
    int l = 0, r = ma;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (check(mid))
            r = mid;
        else {
            if (flag == false)
                return -1;
            l = mid + 1;
        }
    }
    return l;
}

int main() {
    read(n); read(p); read(k);
    for (int i = 1; i <= p; i++) {
        int u, v, cost;
        read(u); read(v); read(cost);
        Add_Edge(u, v, cost);
        ma = max(ma, cost);
    }
    printf("%d\n", Find());
    return 0;
}

你可能感兴趣的:(图论,java,算法,图论,dijkstra)