pku 3662 Telephone Lines 二分+Dijkdtra

http://poj.org/problem?id=3662

开始读这个题目的直接理解错了题意,没想到lengths竟然还有几段的意思;题意求从1到n的每段路径下(除去k各最大的)最长最短问题。

一个无向图有n个点, m条边, 每边有花费val[i], 可以任意指定有k条边免费, 现要求选择若干条边构成顶点1到顶点n的一条路径, 使得路径上边权最大值(不包括免费边)最小. 
 二分答案, 重新建图, Dijkstra

感觉这个讲解比较详细:

我们暂且不考虑最优解的问题, 假设A为一个可行解. 可以知道如果A成立, 则B(B>=A)一定成立. 这说明解具有单调性, 即所有可行解是一个单调序列. 我们模仿二分查找的方法, 对答案进行二分尝试, 对最优解逐步逼近.

对于这道题, 假设A=4这个解成立, 则说明顶点1到N之间必存在某路径, 满足这条路径中长度大于4的边不超过K条(如果超过K条, 4就不是去除K条以外的最大值了, 与题意矛盾).

1. 判断给定的A是否为可行解, 即求从顶点1到顶点N的某条路径上, 长度大于A的边最少有多少条. 如果最少的条数小于等于K, 则A为一个可行解. 
对于求最少的条数, 可以在原图的基础上构造一个新图, 改变边权. 如果一条边原来的权值大于A, 则在新图中将其权值设为1;如果一条边原来的权值小于等于A, 则在新图中将其权值设为0. 
然后求出从顶点1到N的最短路径长度D, D即为从顶点1到顶点N的某条路径上, 长度大于A的边最少有多少条. 如果D<=K, 则A为可行解.

View Code
#include <cstdio>

#include <cstring>

#include <iostream>

#include <algorithm>

#define maxn 1007

#define inf 99999999

using namespace std;



int map[maxn][maxn];

struct node

{

    int x,y;

    int val;

}p[maxn*10];



int n,m,k;

int dis[maxn];

bool vt[maxn];



int cmp(node a,node b)

{

    return a.val < b.val;

}

void init()

{

    int i,j;

    for (i = 0; i <= n; ++i)

    {

        for (j = 0; j <= n; ++j)

        {

            if (i == j) map[i][j] = 0;

            else map[i][j]  = inf;

        }

    }

}



void Dijkstra()

{

    int i,j,min;

    for (i = 1; i <= n; ++i)

    {

        vt[i] = false;

        dis[i] = map[1][i];

    }

    vt[1] = true;

    for (int ki = 1; ki < n; ++ki)

    {

        j = 1; min = inf;

        for (i = 2; i <= n; ++i)

        {

            if (!vt[i] && dis[i] < min)

            {

                j = i; min = dis[i];

            }

        }

        if (min == inf) break;

        vt[j] = true;

        for (i = 2; i <= n; ++i)

        {

            if (!vt[i] && dis[i] > map[i][j] + dis[j] && map[i][j] != inf)

            {

                dis[i] = map[i][j] + dis[j];

            }

        }

    }

}

int main()

{

    //freopen("in.txt","r",stdin);

    int i;

    int x,y,z;

    scanf("%d%d%d",&n,&m,&k);

    init();

    for (i = 1; i <= m; ++i)

    {

        scanf("%d%d%d",&x,&y,&z);

        map[x][y] = map[y][x] = 1;

        p[i].x = x; p[i].y = y;

        p[i].val = z;

    }

//按边的权值排序

    sort(p + 1, p + 1 + m, cmp);

    int l = 0, r = m - 1;

    int ms = 0,ans = -1;

    p[0].val = 0;//注意这里0的处理

    while (l <= r)

    {

        int mid = (l + r)/2;

//ms的左边都是0,右边都是1

        if (ms <= mid)

        {

            for (i = ms; i <= mid; ++i)

            {

                map[p[i].x][p[i].y] = map[p[i].y][p[i].x] = 0;

            }

        }

        else

        {

            for (i = mid + 1; i <= ms; ++i)

            {

                map[p[i].x][p[i].y] = map[p[i].y][p[i].x] = 1;

            }

        }

        ms = mid;

        Dijkstra();

        if (dis[n] > k) l = mid + 1;

        else

        {

            ans = mid; r = mid - 1;

        }

    }

    //printf("%d\n",ans);

    if (ans == - 1) printf("-1\n");

    else printf("%d\n",p[ans].val);

    return 0;

}

 

 

你可能感兴趣的:(pku)