LA4080 删边求最短路

来源:互联网 发布:微信公众号淘宝优惠券 编辑:程序博客网 时间:2024/06/12 01:26

关键词:最短路树、删边重求最短路

删除任意一边,求新图任意两点最短路之和的最大值

1.建立每个点的最短路树

2.枚举删边,判断该边是否在最短路树中。在:删边后更改最短路树;不在:最短路树不变,即该点到任意一点的最短路不变

最短路树作用:删边问题中降低复杂度

原理:每个最短路树仅含n-1条边,降低了每个点的枚举量

每个最短路树中有n-1条边,还有删边属于k的最短路树时,才需要重新求k的最短路树,因此每个顶点最多求n-1次最短路树(即最短路),因此总共最多求n*(n-1)次最短路,复杂度O(n*n*m*logn)

技巧:1.用数组parent记录一棵树

2.链式前向星的好处:1)可以标记边。实现删边、标记特殊边的功能 2)无向图中可以找到任一条边的反向边(i^1是i的反向边)


#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#include<vector>#include<stack>#include<queue>#define ll long long#define INF 0x3f3f3f3f#define maxn 110#define maxm 1010#define mem(a,b) memset(a,b,sizeof(a))using namespace std;int n,m;ll length;struct Edge{    int to,next;    ll w;}edge[maxm<<2];int head[maxn],tot;ll d[maxn][maxn],dcopy[maxn][maxn];int parent[maxn][maxn],parentcopy[maxn][maxn];//i->j的单源最短路经;i最短路树中j的父节点bool cut[maxm<<2],vis[maxn];ll ans1,ans2;struct Heapnode{    int u;    ll d;    bool operator< (const Heapnode&rhs)const{        return d>rhs.d;    }};void add(int u,int v,ll w){    edge[tot].to=v,edge[tot].next=head[u],edge[tot].w=w;    head[u]=tot++;}void build(){    mem(cut,0),mem(head,-1),tot=0;    for(int i=1;i<=m;i++){        int a,b;        ll c;        scanf("%d%d%lld",&a,&b,&c);        add(a,b,c),add(b,a,c);    }}void dij(int s,ll d[],int parent[]){    priority_queue<Heapnode> que;    for(int i=1;i<=n;i++) d[i]=INF;    d[s]=0,mem(vis,0),mem(parent,-1);    que.push((Heapnode){s,0});    while(!que.empty()){        Heapnode x=que.top();que.pop();        int u=x.u;        if(vis[u]) continue;        vis[u]=1;        for(int i=head[u];i!=-1;i=edge[i].next){            if(cut[i]) continue;            int v=edge[i].to; ll w=edge[i].w;            if(d[v]>d[u]+w){                d[v]=d[u]+w;                parent[v]=u;                que.push((Heapnode{v,d[v]}));            }        }    }}ll cal(){    ll ans=0;    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)                if(d[i][j]==INF) ans+=length;                else ans+=d[i][j];    return ans;}void Copyint(int a[][maxn],int b[][maxn]){    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            a[i][j]=b[i][j];}void Copyll(ll a[][maxn],ll b[][maxn]){    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            a[i][j]=b[i][j];}int main(){    //freopen("a.txt","r",stdin);    while(scanf("%d%d%lld",&n,&m,&length)!=EOF){        build();        for(int i=1;i<=n;i++) dij(i,d[i],parent[i]);        ans1=cal(),ans2=0;        Copyll(dcopy,d),Copyint(parentcopy,parent);//记录原始数据        for(int u=1;u<=n;u++){            for(int i=head[u];i!=-1;i=edge[i].next){                cut[i]=cut[i^1]=1;                int v=edge[i].to; ll w=edge[i].w;                for(int k=1;k<=n;k++){                    if((parent[k][v]==u)||(parent[k][u]==v)) { dij(k,d[k],parent[k]); }//如果<u,v>属于k的最短路树,重求最短路树                }                ans2=max(ans2,cal());                Copyll(d,dcopy),Copyint(parent,parentcopy);//还原原始数据                cut[i]=cut[i^1]=0;            }        }        printf("%lld %lld\n",ans1,ans2);    }}




0 0
原创粉丝点击