hdu3986 Harry Potter and the Final Battle(删边最短路/枚举+dijkstra)

题目

T(T<=20)组数据,n(n<=1e3)个点,m(m<=5e4)条边,

每条边都是双向的,从点1到点n的最短路中,

对方会炸毁一条,问你在最坏情况下最短路是多长

题解

先跑一遍dijkstra,用pre[v]和E[v]把前驱的点和边都记录下来

如果不通,就不用枚举删哪条边了

从点n向点1回溯,在回溯的路上枚举删边,

每删一条就跑一次dijkstra,取中途的最大值

由于加边过程中正向和反向是一起加进去的,

和网络流那里的操作类似,edge和edge^1代表了这两条边

 

这里由于pre[1]==0恒成立,不用将pre数组初始化

只要最短路上经过的点,是一定会被更新的,

在新的回溯过程中全部利用的是本次更新的pre和id值,

不用管被上轮更改的值

心得

虽然是自己写的,虽然是1y

但考虑到自己之前最短路的题总是迷之WA,还是总结一下吧

感觉自己最短路终于入了点门了,会改板子了

看来做个10-20题之后,的确对一个领域会有新的认知

代码

#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e3+10;
const int maxm=1e5+10;
typedef long long ll;
typedef pair P;
struct edge
{
	int id;
  	int v,nex;
	ll w;
	bool ok;//有没有被删 
}e[maxm];
priority_queue,greater

>q; int t,n,m,head[maxn],cnt; int pre[maxn],E[maxn]; ll dis[maxn]; ll res; bool vis[maxn]; void init1() { //memset(pre,0,sizeof pre); 总是从1出发,所以pre[1]==0恒成立 memset(head,-1,sizeof head); cnt=0; } void init2(int n) { while(!q.empty())q.pop(); memset(vis,0,sizeof vis); for(int i=1;i<=n;++i) dis[i]=2e18; } void add(int u,int v,ll w,bool ok) { e[cnt].id=cnt; e[cnt].v=v; e[cnt].nex=head[u]; e[cnt].w=w; e[cnt].ok=ok; head[u]=cnt++; } void dijkstra(int s,int op) { dis[s]=0; q.push(P(dis[s],s)); while(!q.empty()) { P tmp=q.top(); q.pop(); ll level=tmp.first; int u=tmp.second; if(vis[u])continue; vis[u]=1; for(int i=head[u];~i;i=e[i].nex) { int v=e[i].v,id=e[i].id; ll w=e[i].w; bool ok=e[i].ok; if(!ok)continue; if(dis[v]>dis[u]+w) { dis[v]=dis[u]+w; if(op)pre[v]=u,E[v]=id; q.push(P(dis[v],v)); } } } } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init1();init2(n); while(m--) { int u,v;ll w; scanf("%d%d%lld",&u,&v,&w); add(u,v,w,1); add(v,u,w,1); } dijkstra(1,1); if(dis[n]==2e18) { puts("-1"); continue; } res=dis[n]; for(int u=n;u;u=pre[u]) { int Edge=E[u]; e[Edge].ok=0; e[Edge^1].ok=0; init2(n); dijkstra(1,0); res=max(res,dis[n]); e[Edge].ok=1; e[Edge^1].ok=1; } printf("%lld\n",res==2e18?-1:res); } return 0; }

 

你可能感兴趣的:(#,最短路/差分约束,最短路,枚举)