csu 1307 最大值最小,

来源:互联网 发布:ubuntu有什么用 编辑:程序博客网 时间:2024/05/16 23:42
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 21 2 801 3 402 3 503 3 1 21 2 901 3 102 3 204 5 1 41 2 81 4 91 3 102 4 73 4 8

Sample Output

903015

初看没思路,细看还是没思路,想想能不能用最小生成树做,后来发现如果从起点到几个点有相同的距离的话,用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
原创粉丝点击