poj 3662 二分+最短路

题意:

给定一个n个点的无向图fr,to,w。

现在从1点到n点,有k条边可以免花费,求免费之后剩下的那些边里面最大花费最小的值是多少。


解析:

首先搞懂题意,从1点到n点可以有很多条路。

假设已经确定了一条路径,则这些边里面花费最大的k个肯定使用免费最赚,然后剩下边里面找到最大的就是当前的这个花费值了。

要使这个花费值最小,就直接假设到这个值去二分。

二分的比较项是大于当前这个花费值的边的条数,如何求这个条数?

将大于花费的边的值在求最短路里面搞成1,然后小于其的搞成0,然后求一个最短路就是条数了。

比较这个条数就能够确定当前花费值的取值过大或者过小了:

若当前条数小于k条,则说明大于花费的条数少了,说明花费值大了;

反之,花费小了。

然后二分的时候大概20次就行了,大了TLE,小了WA。


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long

using namespace std;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = 4 * atan(1.0);
const double ee = exp(1.0);
const int maxn = 1000 + 10;

int n, p, k;
vector< pair<int, int> > g[maxn];

int head[maxn];
bool vis[maxn];
int dis[maxn];
int eNum;

struct Edge
{
    int fr, to, cost;
} e[maxn * maxn];

void addEdge(int fr, int to, int c)
{
    e[eNum].fr = head[fr];
    e[eNum].to = to;
    e[eNum].cost = c;
    head[fr] = eNum++;
}

void buildG(int val)
{
    eNum = 0;
    memset(head, -1, sizeof(head));
    for (int u = 1; u <= n; u++)
    {
        int sz = g[u].size();
        for (int i = 0; i < sz; i++)
        {
            int v = g[u][i].first;
            int w = g[u][i].second;
            if (val < w)
            {
                addEdge(u, v, 1);
                addEdge(v, u, 1);
            }
            else
            {
                addEdge(u, v, 0);
                addEdge(v, u, 0);
            }
        }
    }
}

bool spfa(int val)
{
    buildG(val);
    for (int i = 1; i <= n; i++)
    {
        dis[i] = inf;
        vis[i] = false;
    }
    stack<int> s;
    s.push(1);
    vis[1] = true;
    dis[1] = 0;
    while (!s.empty())
    {
        int cur = s.top();
        s.pop();
        for (int i = head[cur]; i != -1; i = e[i].fr)
        {
            int x = e[i].to;
            if (dis[cur] + e[i].cost < dis[x])
            {
                dis[x] = dis[cur] + e[i].cost;
                if (!vis[x])
                {
                    vis[x] = true;
                    s.push(x);
                }
            }
        }
        vis[cur] = false;
    }
//    cout << dis[n] << endl;
    return dis[n] <= k;
}

int main()
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
#endif // LOCAL
    while (~scanf("%d%d%d", &n, &p, &k))
    {
        for (int i = 1; i <= n; i++)
            g[i].clear();
        int maxx = 0;
        while (p--)
        {
            int fr, to, w;
            scanf("%d%d%d", &fr, &to, &w);
            g[fr].push_back(make_pair(to, w));
            maxx = maxx < w ? w : maxx;
        }
        int lo = 0, hi = maxx;
        for (int i = 0; i < 20; i++)
        {
            int mi = (lo + hi) >> 1;
            if (spfa(mi))
                hi = mi;
            else
                lo = mi;
        }
        if (hi == maxx)
            puts("-1");
        else
            printf("%d\n", hi);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值