【题解】poj3662 dijkstra+二分

题目链接

题目大意

在无向图上求出一条从1到n的路径,使得路径上第k+1大的边权尽量小。

思路

本题答案显然具有单调性,因为支付的钱更多时,合法的升级方案一定包含了花费更少的升级方案。所以我们可以二分答案,把问题转化为:是否存在一种合法的升级方法,使花费不超过mid。
将价格大于mid的电缆看做长度为1,把升级价格不超过mid的电缆看做长度为0,求1到N的最短路不超过K。
可以用双端队列BFS求解这种边权只有0和1的最短路问题。(李煜东《算法竞赛进阶指南》)
在这里采用的是dijkstra算法,二分部分在参考大佬博客修改后AC。

#include
#include
#include
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
const int N=1e3+10;
const int M=1e4+10;
int n,p,k;
struct Edge{
    int v,w,nx;
}edge[M<<1];
int head[N],tot;
void addedge(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nx=head[u];
    head[u]=tot++;
}
int dis[N];
bool vis[N];
struct node{
    int v,w;
    node(int _v=0,int _w=0):v(_v),w(_w){}
    bool operator<(const node&rhs)const{
    return w>rhs.w;}
};
priority_queueq;
bool dijkstra(int mid)
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,false,sizeof(vis));
    dis[1]=0;
    q.push(node(1,0));
    while(!q.empty())
    {
        node tmp=q.top();q.pop();
        int u=tmp.v;
        vis[u]=true;
        for(int i=head[u];~i;i=edge[i].nx)
        {
            int v=edge[i].v;
            if(vis[v])continue;
            int w=edge[i].w>=mid?1:0;
            if(dis[v]>w+dis[u])
            {
                dis[v]=w+dis[u];
                q.push(node(v,dis[v]));
            }
        }
    }
    return dis[n]>=k+1;
}
int main()
{
    //freopen("in.txt","r",stdin);
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&p,&k);
    int u,v,w;
    _rep(i,1,p)scanf("%d%d%d",&u,&v,&w),addedge(u,v,w),addedge(v,u,w);
    int l=0,r=1000002,ans=-1;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(dijkstra(mid))l=mid+1,ans=mid;else r=mid-1;
    }
    if(ans>1000000)ans=-1;
    if(r<0)ans=0;
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(最短路问题,poj,算法艺术与信息学竞赛,二分,dijkstra,二分)