描述:https://www.luogu.com.cn/problem/P2176
每天早晨,FJ从家中穿过农场走到牛棚。农场由 N 块农田组成,农田通过 M 条双向道路连接,每条路有一定长度。FJ 的房子在 1 号田,牛棚在 N 号田。没有两块田被多条道路连接,以适当的路径顺序总是能在农场任意一对田间行走。当FJ从一块田走到另一块时,总是以总路长最短的道路顺序来走。
FJ 的牛呢,总是不安好心,决定干扰他每天早晨的计划。它们在 M 条路的某一条上安放一叠稻草堆,使这条路的长度加倍。牛希望选择一条路干扰使得FJ 从家到牛棚的路长增加最多。它们请你设计并告诉它们最大增量是多少。
很明显的最短路。我们可以选择一条路来加倍边权。
简单,我会暴力枚举(●ˇ∀ˇ●)
那样会超时的
考虑哪些边是不用枚举的。
如果没有把稻草放在最初FJ走的最短路上,那一定是无效的,毕竟FJ只走最短路。
那我们就需要记录最短路的路径,然后枚举最短路经过的边,再跑最短路。
if(dis[e.to]>dis[ans.num]+e.w){ pre[e.to]=i;fr[e.to]=ans.num;//节点前驱
在原来的基础上加了这个。fr数组记录的是目标点的前驱。最后一个目标点是n,那么我们可以一直顺着上去找到1.
pre数组记录的是每个节点松弛时对应的边,毕竟我们要对边操作。
int now=n,nu=0; while(now!=1){ that[++nu]=pre[now]; now=fr[now]; }
然后我们开始倒序装进that数组。
枚举边进行修改边权时,由于是无向边,所以正反两边都要修改。
比如路径记录的是编号为3的边,那么其实编号4的边也要修改。记录的是4编号3也要修改。
为什么呢?建图的时候就是两条边连着存嘛~
#include<iostream> #include<cstring> #include<cstdio> #define M 15000 #include<queue> #define N 110 using namespace std; int n,m; int po,ans; int head[N],to[M],next[M],len[M],e=1; void buid(int u,int v,int l) { next[++e]=head[u],head[u]=e; to[e]=v,len[e]=l; } int dis[N],init[N]; int pre[N],fr[N],that[M],nu; queue<int> q; void spfa(int s) { memset(dis,20,sizeof(dis)); dis[s]=0;init[s]=1,q.push(s); while(!q.empty()) { int now=q.front();q.pop();init[now]=0; for(int i=head[now];i;i=next[i]) { int j=to[i]; if(dis[j]>dis[now]+len[i]) { dis[j]=dis[now]+len[i]; pre[j]=i;fr[j]=now; if(!init[j]) { init[j]=1;q.push(j); } } } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { int u,v,l; scanf("%d%d%d",&u,&v,&l); buid(u,v,l); buid(v,u,l); } spfa(1);po=dis[n]; int now=n; while(now!=1) { that[++nu]=pre[now];//记路径 now=fr[now]; } for(int i=1;i<=nu;++i)//枚举路径 { len[that[i]]*=2; len[that[i]^1]*=2; spfa(1);//操♂作 ans=max(ans,dis[n]); len[that[i]]/=2; len[that[i]^1]/=2; } cout<<ans-po<<endl;//end return 0; }