二分+最短路 dijkstra

洛谷p1462 通往奥格瑞玛的道路

对于我这个只是听过二分但是从来没有写过二分的人来说,看到这个题目时是懵逼的,啥是求最多一次收费的最小值,只能回头开始学习二分,在一下几种情况下,均可以用二分来解决。

1.从有序数组中查找某个值。

2.假定一个解并判断是否可行

3.最大化最小值

4.最大化平均值

这个题目中涉及到的就是第三种情况。

再次重新说一下题意,给定起点和终点,设为1和n,那么从1到n有很多条路,我们设每一条路径为ai,因为每个路径经过的点的集合不一样,并且经过每一个点都要被收取那个点所对应的过路费,因为每个点的过路费不同,所以我们把对于每一条路径ai,都有一个过路费最高的点,那么我们设这个最高的点的过路费为f(ai),那么假设这个图中有m条路,那么我们要做的就是求f(a1)、f(a2)······f(am)中最小的那个值,这个就是最大化最小值。

解题思路:

那么根据求什么就二分什么来看(大部分情况下),我们这一题就应该二分费用。首先我们需要把所有点的费用进行排序,剩下的就是二分了,为了方便理解,这里先说最短路应该怎么处理。首先最基本的dijkstra我这里就不说了,我用的是优先队列化简,我在这里就说一下dijkstra在这一题当中需要做什么改动,我们二分的是费用,并且是每一条路中最大的费用,所以在跑dijkstra的时候当某个点的费用大于我们设定的这个费用的时候,这个点就是无效的,即为不能取,所以我们只需要在刚开始的时候把这些点全部标记一遍就可以了,我这里用的是把所有点的状态变为BLACK(即为不能选)。

在知道了如何求最短路之后我们只需要不断的二分这个最大费用就可以了,这个就直接看代码吧,最基本的二分。

 1   while(head<=end)
 2   {
 3     int mid=(head+end)/2;
 4     if(dijkstra(f[mid]))
 5     {
 6         ans=mid;
 7         end=mid-1;
 8     }
 9     else
10     head=mid+1;
11   }

接下来就是ac代码

 

  1 #include <iostream>
  2 #include <cstring>
  3 #include <string>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <stack>
  7 #include <stdio.h>
  8 #include <cmath>
  9 #include <string.h>
 10 
 11 using namespace std;
 12 #define ll long long
 13 static const int WHITE=0;
 14 static const int GRAY=1;
 15 static const int BLACK=2;
 16 static const int INFTY=(1<<30);
 17 vector<pair<int,int> > adj[10005];
 18 int n,m,b;
 19 int f[10005],fi[10005];
 20 bool dijkstra(int k)
 21 {
 22     if(fi[1]>k||fi[n]>k)//如果起点和终点都不满足,这条路径就不满足
 23     return false;
 24     priority_queue<pair<int,int> > que;
 25     int color[10005],d[10005];
 26     for(int i=1;i<=n;i++)//初始化
 27     {
 28         d[i]=INFTY;
 29         color[i]=WHITE;
 30     }
 31     for(int i=1;i<=n;i++)//将不满足该路径条件的点排除
 32     {
 33         if(fi[i]>k)
 34         color[i]=BLACK;
 35     }    
 36     d[1]=0;
 37     que.push(make_pair(0,1));
 38     color[1]=GRAY;
 39     while (!que.empty())//模板
 40     {
 41         pair<int,int> f=que.top();
 42         que.pop(); 
 43         int u=f.second;
 44         color[u]=BLACK;
 45         if(d[u]<f.first*(-1))
 46         continue;
 47         for(int j=0;j<adj[u].size();j++)
 48         {
 49             int v=adj[u][j].first;
 50             if(color[v]==BLACK)
 51             continue;
 52             if(d[v]>d[u]+adj[u][j].second)
 53             {
 54                 d[v]=d[u]+adj[u][j].second;
 55                 que.push(make_pair(d[v]*(-1),v));
 56                 color[v]=GRAY;
 57             }
 58         }
 59     }
 60     if(d[n]>=b)
 61     return false;       
 62     else
 63     return true;
 64 }
 65 int main()
 66 {
 67   scanf("%d%d%d",&n,&m,&b);
 68   for(int i=1;i<=n;i++)
 69   {
 70       scanf("%d",&f[i]);
 71       fi[i]=f[i];
 72   }
 73   for(int i=1;i<=m;i++)//邻接表建图
 74   {
 75     int a,b,c;
 76     scanf("%d%d%d",&a,&b,&c);
 77     adj[a].push_back(make_pair(b,c));
 78     adj[b].push_back(make_pair(a,c));
 79   }
 80   sort(f+1,f+n+1);
 81   int head=1,end=n;
 82   int ans=INFTY;
 83   if(!dijkstra(f[n]))//如果最大的费用都不通的话,一定不通
 84   {
 85       printf("AFK\n");
 86       return 0;
 87   }
 88   while(head<=end)//二分
 89   {
 90     int mid=(head+end)/2;
 91     if(dijkstra(f[mid]))
 92     {
 93         ans=mid;
 94         end=mid-1;
 95     }
 96     else
 97     head=mid+1;
 98   }
 99     printf("%d\n",f[ans]);
100   return 0;
101 } 

 

posted @ 2019-10-18 20:11  Linkss  阅读(268)  评论(0编辑  收藏  举报