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分钟内是就能通过。
输出格式:
输出文件的第一行中写出用分钟表示的最长时间,在这段时间中,无论哪条路在堵车,玛丽卡应该能够到达麦克处,如果少于这个时间的话,则必定存在一条路,该条路一旦堵车,玛丽卡就不能够赶到麦克处。
输入输出样例
5 7 1 2 8 1 4 10 2 3 9 2 4 10 2 5 1 3 4 7 3 5 10
27
初读题目,告诉我们,女朋友有风险,耍朋友须谨慎。
如果你还是没有思路,或者是迷迷糊糊。
应该!多读几遍题。
这道题说的是:最短路线里面的最长时间。就是删除一条边,之后找最短路径。
所有找到每一条最短路径里的时间最长的。
每删一条边,都要spfa一边找到最短的路径,之后比较。
算一道很裸的题?不过。这里的难点在语文。要理解!!
帮助麦克找出玛丽卡按->最短路线->通过不堵车道路->到达他所在城市所需的->最长时间
而代码实现,其实就是最开始一遍spfa。用h数组来记录,当前的点在松弛时,最优的那条边。记录边!!!
之后从终点开始,往回找。由于h里有当前节点前面的边。所以可以找到这个图的最短路径。之后呢,删最短路径就是保证最短路的整体还是有就是堵车的时候不能走最短路,最后也要步走这条边也要找到最短路。而且找的时候专除小的边。
1)会T一个点
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct node { int next,v,t,val; } edge[1000001]; int heads[1000001],d[1010],cnt,head,last,visit[1010],h[1010]; int q[10001];//手写队列 int n,m; int add(int x,int y,int z) //领接表,里面有起点!这个重要。 { edge[++cnt].next=heads[x]; edge[cnt].v=y; edge[cnt].t=x; edge[cnt].val=z; heads[x]=cnt; } int spfa(int x) { memset(d,0x7f,sizeof(d)); d[1]=0; int t=edge[x].val; edge[x].val=0xffff;//删边 head=1; last=1; q[last]=1; visit[1]=1; while(head<=last) { int a=q[head]; for(int i=heads[a]; i!=-1; i=edge[i].next) { if(d[edge[i].v]>d[a]+edge[i].val) { if(!visit[edge[i].v]) { q[++last]=edge[i].v; visit[edge[i].v]=0; d[edge[i].v]=d[a]+edge[i].val; } else { d[edge[i].v]=d[a]+edge[i].val; } } } head++; visit[a]=0; } edge[x].val=t; } int main() { memset(d,2147,sizeof(d)); memset(heads,-1,sizeof(heads)); int x,y,z; scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } head=1; last=1; q[last]=1; visit[1]=1; d[1]=0; while(head<=last) { int a=q[head]; for(int i=heads[a]; i!=-1; i=edge[i].next) { if(d[edge[i].v]>d[a]+edge[i].val) { if(!visit[edge[i].v]) { q[++last]=edge[i].v; visit[edge[i].v]=0; d[edge[i].v]=d[a]+edge[i].val; h[edge[i].v]=i; //记录每个点最短路松弛时的边 } else { d[edge[i].v]=d[a]+edge[i].val; h[edge[i].v]=i;//同上 } } } head++; visit[a]=0; } int b=d[n]; for(int i=n; i!=1; i=edge[h[i]].t) //枚举从终点一直到起点,删最短路的一条边。 { spfa(h[i]); b=max(b,d[n]); } printf("%d",b); return 0; }
2)AC
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int D = 1001; const int S = 1e6 + 10; const int M = 2e6; struct orz { int next,v,t,val; } edge[S]; int heads[S],d[D],cnt; int v[D],h[D]; int n,m; bool cannot[M]; inline long long read() { int x=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1;ch=getchar();} while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar();} return x*f; } int add(int x,int y,int z) ///邻接表,里面有起点!这个重要. { edge[++cnt].next=heads[x]; edge[cnt].t=x; edge[cnt].v=y; edge[cnt].val=z; heads[x]=cnt; } int spfa(bool flag) { memset(d,0x7f7f7f,sizeof(d)); memset(v,0,sizeof(v)); queue<int>q; q.push(1); v[1]=1; d[1]=0;///麦克起点为1 while(!q.empty()) { int a=q.front(); q.pop(); v[a]=0; for(int i=heads[a]; i!=-1; i=edge[i].next) { int vv=edge[i].v,val=edge[i].val; if(cannot[a*1001+vv]) continue; if(d[vv]>d[a]+val) { d[vv]=d[a]+val; if(flag) h[vv]=a; if(!v[vv]) { q.push(vv); v[vv]=1; } } } } } int main() { memset(heads,-1,sizeof(heads)); int a,b,c; n=read();m=read(); for(int i=1;i<=m;i++) { a=read(),b=read(),c=read(); add(a,b,c),add(b,a,c); } spfa(1); int ans=d[n]; int y=n; while(y) { cannot[y*1001+h[y]]=1; cannot[h[y]*1001+y]=1; spfa(0); if(d[n]>ans) ans=d[n]; cannot[y*1001+h[y]]=0; cannot[h[y]*1001+y]=0; y=h[y]; } printf("%d ",ans); return 0; }