P1186 玛丽卡

Posted 范仁义

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1186 玛丽卡相关的知识,希望对你有一定的参考价值。

P1186 玛丽卡

题目描述

麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复。

因为她和他们不住在同一个城市,因此她开始准备她的长途旅行。

在这个国家中每两个城市之间最多只有一条路相通,并且我们知道从一个城市到另一个城市路上所需花费的时间。

麦克在车中无意中听到有一条路正在维修,并且那儿正堵车,但没听清楚到底是哪一条路。无论哪一条路正在维修,从玛丽卡所在的城市都能到达麦克所在的城市。

玛丽卡将只从不堵车的路上通过,并且她将按最短路线行车。麦克希望知道在最糟糕的情况下玛丽卡到达他所在的城市需要多长时间,这样他就能保证他的女朋友离开该城市足够远。

编写程序,帮助麦克找出玛丽卡按最短路线通过不堵车道路到达他所在城市所需的最长时间(用分钟表示)。

输入输出格式

输入格式:

 

第一行有两个用空格隔开的数N和M,分别表示城市的数量以及城市间道路的数量。1≤N≤1000,1≤M≤N*(N-1)/2。城市用数字1至N标识,麦克在城市1中,玛丽卡在城市N中。

接下来的M行中每行包含三个用空格隔开的数A,B和V。其中1≤A,B≤N,1≤V≤1000。这些数字表示在A和城市B中间有一条双行道,并且在V分钟内是就能通过。

 

输出格式:

 

输出文件的第一行中写出用分钟表示的最长时间,在这段时间中,无论哪条路在堵车,玛丽卡应该能够到达麦克处,如果少于这个时间的话,则必定存在一条路,该条路一旦堵车,玛丽卡就不能够赶到麦克处。

 

输入输出样例

输入样例#1:
5 7
1 2 8
1 4 10
2 3 9
2 4 10
2 5 1
3 4 7
3 5 10
输出样例#1:
27
 

分析:

方法一:
spfa+spfa

先SPFA求出最短路,然后枚举去掉哪条边,再跑n次spfa,记录结果得出最大的方案。

这道题可以用图论求最短路解决,第一次跑最短路时记录最短路上的每条边(这里的边是无向的),然后再依次删除最短路上的每一条边(因为删除其他边最短路不变,所以只需删最短路上的便就行了),再跑SPFA。

注意!!!SPFA再这道题上会有一个点超时,这里介绍一种简单而又实用的优化方法

SLF优化:将某个点加入队列是,设当前点为 v,队首元素为u,若dist[v] < dist[u],即把 v 加入到队列的头部,否则加入到队列尾部。

用双端队列deque实现。

优化思想就是:利用价值最大的点最先处理

 

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<deque>
  5 #include<cstring>
  6 
  7 using namespace std;
  8 
  9 struct edge
 10 {
 11     int v;
 12     int next;
 13     int w;
 14 }e[1000050];
 15 
 16 int ans,dist[1005],p[1005],head[1005],cnt,delu,delv;
 17 bool vis[1005];
 18 
 19 void add(int u,int v,int w)//添加边
 20 {
 21     e[++cnt] = (edge){v,head[u],w};
 22     head[u] = cnt;
 23 }
 24 
 25 int read() //读入优化 
 26 {
 27     int ans=0;char ch = getchar();
 28     while(ch<0||ch>9)    ch = getchar();
 29     while(ch>=0&&ch<=9) {ans = ans *10 + ch - 0;ch = getchar();}
 30     return ans;
 31 }
 32 
 33 void spfa()
 34 {
 35     int v,u;
 36     deque<int> q;//双向队列
 37     memset(dist,-1,sizeof(dist));
 38     memset(vis,false,sizeof(vis));
 39     q.push_back(1);
 40     dist[1] = 0;
 41     vis[1] = true;
 42     while(!q.empty())
 43     {
 44         u = q.front();
 45         q.pop_front();
 46         for(int i=head[u];i;i=e[i].next)
 47         {
 48             v = e[i].v;
 49             if(dist[v]<0 || dist[v]>dist[u]+e[i].w)
 50             {
 51                 if(!(u==delu&&v==delv)||(v==delu&&u==delv))//判断边是否被删除
 52                 {
 53                     dist[v] = dist[u] + e[i].w;
 54                     if(!ans)//如果已经跑过一次最短路就不要记录边了
 55                         p[v] = u;
 56                     if(!vis[v])
 57                     {
 58                         if(!q.empty())
 59                         {
 60                             if(dist[v]<dist[q.front()])
 61                                 q.push_front(v);
 62                             else
 63                                 q.push_back(v);
 64                         }
 65                         else
 66                             q.push_back(v);
 67                         vis[v] = true;
 68                     }
 69                 }
 70             }
 71         }
 72         vis[u] = false;
 73     }
 74 }
 75 
 76 int main()
 77 {
 78     int n,m;
 79     n=read();
 80     m=read(); 
 81     for(int i=1;i<=m;i++)
 82     {
 83         int u,v,w;
 84         u=read();
 85         v=read();
 86         w=read();
 87         add(u,v,w);
 88         add(v,u,w);
 89     }
 90     spfa();
 91     ans = dist[n];
 92     for(int i = n;i!=1;i = p[i])
 93     {
 94         delu = p[i];//依次删除最短路上的边
 95         delv = i;
 96         spfa();
 97         ans = max(dist[n],ans);
 98     }
 99     printf("%d",ans);
100     return 0;
101 }

 

 

方法二:

堆优化的dijkstra(用优先队列实现)

先dijkstra求出最短路,然后枚举去掉哪条边,再跑n次dijkstra,记录结果得出最大的方案。

 
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cmath>
 4 #include <cstdlib>
 5 #include <algorithm>
 6 #include <string>
 7 #include <vector>
 8 #include <queue>
 9 #include <iostream>
10 using namespace std;
11 const int maxn=1000+20,inf=1234567890;
12 typedef pair<int,int> P;
13 struct edge{int to,w;};
14 vector<edge>G[maxn];
15 int n,m,pre[maxn],d[maxn];
16 priority_queue<P,vector<P>,greater<P> >que;
17 void read(int &n){
18     n=0;int f=1;
19     char ch=getchar();
20     while(ch<0 || ch>9){
21         if(ch==-) f=-1;
22         ch=getchar();
23     }
24     do{
25         n=n*10+ch-0;
26         ch=getchar();
27     }while(ch>=0 && ch<=9);
28     n=n*f;
29     return;
30 }
31 void write(int n){
32     if(n<0){
33         putchar(-);
34         n=0-n;
35     }
36     if(n>=10) write(n/10);
37     putchar((n % 10)+0);
38     return;
39 }
40 inline void add_edge(int u,int v,int w){
41     edge e;e.to=v;e.w=w;
42     G[u].push_back(e);
43     return;
44 }
45 void init(){
46     read(n);read(m);
47     for(int i=1;i<=m;i++){
48         int u,v,w;
49         read(u);read(v);read(w);
50         add_edge(u,v,w);
51         add_edge(v,u,w);
52     }
53     return;
54 }
55 int dijkstra(int f,int ff){
56     for(int i=1;i<=n;i++) d[i]=inf;
57     d[1]=0;
58     P p(0,1);que.push(p);
59     while(!que.empty()){
60         P temp=que.top();
61         que.pop();
62         int u=temp.second;
63         if(d[u]<temp.first) continue;
64         for(int i=0;i<G[u].size();i++){
65             int v=G[u][i].to;
66             if(f==u && ff==v) continue;
67             if(ff==u && f==v) continue;
68             int w=G[u][i].w;
69             int t=d[u]+w;
70             if(d[v]>t){
71                 d[v]=t;
72                 que.push(P(t,v));
73                 if(!f && !ff) pre[v]=u;
74             }
75         }
76     }
77     return d[n];
78 }
79 void solve(){
80     memset(pre,-1,sizeof(pre));
81     int ans=dijkstra(0,0);
82     int u=n;
83     while(pre[u]!=-1){
84         int r=dijkstra(u,pre[u]);
85         if(r!=inf) ans=max(ans,r);
86         u=pre[u];
87     }
88     write(ans);
89     return;
90 }
91 int main(){
92     init();
93     solve();
94     return 0;
95 }

 

 
 
 

以上是关于P1186 玛丽卡的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P1186 玛丽卡

Luogu P1186 玛丽卡

P1186 玛丽卡 --- 最短路

P1186 玛丽卡

P1186 玛丽卡

P2176路障与P1186玛丽卡与P1491集合位置全面胜利