csu 1307 最大值最小,

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 550   Solved: 125
[ Submit][ Status][ Web Board]

Description

Alice想要从城市A出发到城市B,由于Alice最近比较穷(不像集训队陈兴老师是个rich second),所以只能选择做火车从A到B。不过Alice很讨厌坐火车,火车上人比较多,比较拥挤,所以Alice有很严格的要求:火车的相邻两站间的最大距离尽可能的短,这样Alice就可以在停站的时候下车休息一下。当然Alice希望整个旅途比较短。

Input

有多组测试数据。
每组测试数据的第一行有两个整数N,M,A,B(N<=2000, M<=50000, N >=2, A,B<=N),其中N是城市的个数,M是城市间通火车的个数。
A,B是Alice起始的城市与目的地城市,城市的标号从1开始。
接下来的M行每行三个整数u,v,w表示从u到v和从v到u有一条铁路,距离为w, u,v<=N, w<=10000。

Output

对于每组测试数据输出满足Alice要求的从A到B的最短距离。

Sample Input

3 3 1 2
1 2 80
1 3 40
2 3 50
3 3 1 2
1 2 90
1 3 10
2 3 20
4 5 1 4
1 2 8
1 4 9
1 3 10
2 4 7
3 4 8

Sample Output

90
30
15

初看没思路,细看还是没思路,想想能不能用最小生成树做,后来发现如果从起点到几个点有相同的距离的话,用prime也不好算。其实题目的意思可以转化成:去掉较长的边,看能否通过其他较之短的边而达到。譬如从A到B有多条路,一条是直达的路,DA-B=35,一条是先到C,DA-C15,然后由C到B,距离是30.所以按题意应该是先到C,再到B。此时,如果有C到F,DC-F=20,然后由F到B,DF-B=25,那么他应该走这样的路线:A---C---F---B;

所以可以这么考虑:设边的最大长度为DMAX,然后,逐渐减小DMAX的长度,如果减小后用SPFA依旧能算出距离D!=无穷大,说明该减小的操作是正确的,然后再减小。。。但如果用SPFA算出距离D为无穷大,说明减小到该极限长度后,出发点和目的地无法到达。此时就应该扩大DMAX的长度.
如果用暴力的话,会超时,所以得用二分了,二分最大边和最小边。
如果是最小值最大的话,应该也是这种上路,逐渐真大Dmin,然后balabala.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxm=100000;
const int inf=0x3f3f3f3f;
int next[maxm],inq[maxm],first[maxm],v[maxm],w[maxm],d[maxm],dis[maxm];
int n,a1,b1,m,i,s,ed,w1,tmp;
int e;
int l,r,mid;
int cmp(int x,int y)
{
    return x>y;
}
void init()
{
    e=0;
    memset(first,-1,sizeof(first));
}
void add_edge(int a,int b,int c)
{
    w[e]=c;v[e]=b;next[e]=first[a];first[a]=e++;
}
int spfa(int x,int y)
{
    queue<int >q;
    memset(d,0x3f,sizeof(d));
    d[x]=0;inq[x]=1;
    q.push(x);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(i=first[u];i!=-1;i=next[i])
        if(w[i]<=mid)
        {
           if(d[v[i]]>d[u]+w[i])
           {
            d[v[i]]=d[u]+w[i];
           if(!inq[v[i]])
              q.push(v[i]),inq[v[i]]=1;
           }
        }

    }
return d[y];
}
//*   用二分的思想去递减
int main()
{
    int ans1,ans2,ans;
    while(cin>>n>>m>>a1>>b1)
    {
       memset(inq,0,sizeof(inq));
      init();
      l=1<<30;
      r=-1;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&s,&ed,&w1);
            add_edge(s,ed,w1);
            add_edge(ed,s,w1);
            l=min(l,w1);//选出最小的边
            r=max(r,w1);//选出最大的边

        }
        //l=1<<30;r=-1;
        mid=(l+r)>>1;
//用二分的关键思路在于:把一些长的边“舍去”,看看能否通过较短的几条边到达。
//如果能,说明该边的却可以舍去,如果不能,说明应该扩大边的最大长度,
//即在二分的时候,选择右半边。
       while(l<=r)
       {
           tmp=spfa(a1,b1);
           //cout<<"tmp"<<tmp<<endl;
          if(tmp==0x3f3f3f3f)
               l=mid+1;
          else
              r=mid-1,ans=tmp;

          mid=(l+r)>>1;

       }
       cout<<ans<<endl;

    }
}
/*用暴力看看能不能实现,结果会超时
int main()
{
     int ans1,ans2,ans,j;
    while(cin>>n>>m>>a1>>b1)
    {
       memset(inq,0,sizeof(inq));
      init();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&s,&ed,&w1);
            add_edge(s,ed,w1);
            add_edge(ed,s,w1);
            dis[i]=w1;
        }
        sort(dis+1,dis+m+1,cmp);
    for(j=1;j<=m;j++)
       {
           //cout<<"dis"<<j<<"="<<dis[j]<<endl;
           mid=dis[j];
           tmp=spfa(a1,b1);
           //cout<<tmp<<endl;
          if(tmp==inf)
          {
              mid=dis[j-1];
              tmp=spfa(a1,b1);
              break;
          }
       }

      cout<<tmp<<endl;
}
return 0;
}*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值