P1948 [USACO08JAN]电话线Telephone Lines

传送门

思路:

  二分+最短路径:可以将长度小于等于 mid 的边视为长度为 0 的边,大于 mid 的边视为长度为 1 的边,最后用 dijkstra 检查 d [ n ] 是否小于等于 k 即可。

标程:

#include<cstring>
#include<queue>
#include<cstdio>
#include<iostream>
#include<vector>
#include<fstream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<stack>
#include<map>
#include<set>
#include<deque>
#include<string>
using namespace std;
#define maxn 100100
#define INF 0x3f3f3f3f
priority_queue<pair< int,int >,vector<pair<int,int> >,greater<pair<int,int > > > q;
struct hh
{
    int u,v,w,nex;
}t[maxn<<1];
int d[maxn<<1],head[maxn<<1],jla[maxn<<1];//jla记录每条边的边长 
int n,m,p,s=1,cnt=0,ans=INF;//ans记录答案 
bool vis[maxn<<1];
inline int read()
{
    int kr=1,xs=0;
    char ls;
    ls=getchar();
    while(!isdigit(ls))
    {
        if(ls=='-') 
            kr=-1;
        ls=getchar();
    }
    while(isdigit(ls))
    {
        xs=(xs<<1)+(xs<<3)+(ls^48);
        ls=getchar();
    }
    return kr*xs;
}
inline void add(int x,int y,int z)
{
    t[++cnt].u=x;
    t[cnt].v=y;
    t[cnt].w=z;
    t[cnt].nex=head[x];
    head[x]=cnt;
}//链式前向星加边 
inline bool dijkstra(int now)
{
    for(int i=1;i<=n;i++)
    {
        d[i]=INF;
        vis[i]=false;
    }
    d[s]=0;
    q.push(make_pair(d[s],s));
    while(!q.empty())
    {
        int k=q.top().second;
        q.pop();
        if(vis[k]) continue;
        else
        {
            vis[k]=true;
            for(int i=head[k];i!=-1;i=t[i].nex)
            {
                if(t[i].w>now)
                    if(d[t[i].v]>d[k]+1)
                    {
                        d[t[i].v]=d[k]+1;
                        q.push(make_pair(d[t[i].v],t[i].v));
                    }//如果t[i].v的边大于当前二分的答案,那么就要使用一次名额
                if(t[i].w<=now)
                    if(d[t[i].v]>d[k])
                    {
                        d[t[i].v]=d[k];
                        q.push(make_pair(d[t[i].v],t[i].v));
                    }//如果t[i].v的边不大于当前二分的答案,那么就无需使用名额,直接松弛操作
            }
        }
    }
    if(d[n]>p) return false;//如果到达n的次数大于免费的次数限制,那么当前答案不可行
    return true;//反之,则可行 
}
int main()
{
    n=read();m=read();p=read();
    for(int i=1;i<=n;i++)
      head[i]=-1;
    int xx,yy,zz;
    for(int i=1;i<=m;i++)
    {
        xx=read();yy=read();zz=read();
        add(xx,yy,zz);
        add(yy,xx,zz);
        jla[i]=zz;
    }
    sort(jla+1,jla+m+1);//将所有的边从小到大排序,为二分做准备
    if(dijkstra(0))
    {
        printf("0\n");
        return 0;
    }
    if(!dijkstra(jla[m]))
    {
        printf("-1\n");
        return 0;
    }//两个特判,,, 
    int l=1,r=m;
    while(l<=r)
    {
        int mid=l+r>>1;//二分查找 
        if(dijkstra(jla[mid]))
        {
            ans=min(ans,jla[mid]);
            r=mid-1;
        }
        else l=mid+1;
    }
    printf("%d\n",ans);//输出答案 
return 0;
}

 

  

posted @ 2018-09-22 21:01  落笔映惆怅丶  阅读(144)  评论(0编辑  收藏  举报