HDU 2433 最短路

这道题是一道很巧妙的最短路。题目大致的意思就是给定一张无向图,枚举每条边删去,求剩下的图中每个
点对距离之和。
//
2433  Travel  删边+最短路之和(预处理桥边)★★★
//
注意处理重边
//这道题目标程的关键在于两点,第一点在于把所有的最近点对拆分成n个顶点到剩余点的n棵最短路径树,
//这样子把整体的不容易去表示的拆分成n个点去跑最短路,使得简化,
//这样这道题目就已经简化成了通过某一个顶点枚举删去一条边,求剩下的最短路之和,第二点就是在不必每删去
//一条求一次最短路,求出求出最短路径树之后有n-1条边存在于树上,删去其他的边没有影响。这个问题就拆分成
//了预处理n棵最短路径树,总共存在n*n-1边 然后跑这么多次最短路,复杂度降为了 n*n*m.
//总结一下,将一个整体分拆成一些部分,对每个部分先进行预处理。这样通过枚举最短路径树上的边,这样
//子就大大减少了重复计算的次数。对最短路的理解还不够透彻。
//标程算法:仍然使用上面的思路,但要作一些预处理。对每个顶点u求一次单源最短路,把求得的结果称作u的最
//短路径树,并用数组记录所有点到 其他所有顶点的路径的和。若被破坏的公路不在该最短路径树上,则从u出发
//的所有最短路径的总和就是u到该树上的所有顶点的路径的总和,因为刚刚记录了这个 数值,因此花费O(1)时间
//就能返回结果。否则,删除被破坏的公路后,
//重新通过BFS计算从u出发的所有最短路径的总和,
//还要判断被破坏的公路在不在这条最短路径树上。
//所以只能在每次删除边的时候,做一些改变。
//具体的做法是:
//对每一个点求一次最短路,并将其求和,保存在一个数组里头,定为sum[i],i表示着一个点到所有其他点最短路
//之和。
//并将这些和相加 ans = sum[1]  + …… + sum[n];
//然后,删除一条边,其顶点暂定为u,v,对这条边的一个顶点u在一次求最短路,如果这个点,不能到达这条边的
//另一个点v,则 直接输出INF如果,能够到达,则对v也求一次最短路,对于u,v两点来说,求得u到每一个点的最
//短路之和sum_u,求得v到每一个点的最短路之和sum_v,
//最后结果为: ans = ans + sum_u + sum_v - sum[u] - sum[v];(我觉得这么写不大对吧)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define maxn 200
#define maxm 8000
#define inf 80000
using namespace std;

//去掉桥边的处理就对了,好像是我桥边处理的问题。

int g[maxn][maxn];
bool vis[maxn];
int head[maxn],low[maxn];
int n,m,cnt,time;
bool flag[maxn][maxn][maxn];///i,u,v;点i用到边u,v;

struct Edge
{
 int from,to,next;
 bool sign;
}edge[2*maxm];

void addedge(int a,int b)
{
    edge[cnt].from=a;
    edge[cnt].to=b;
    edge[cnt].next=head[a];
    head[a]=cnt++;
    edge[cnt].sign=false;
}

void dfs(int u,int fa)
{
    vis[u]=true;
    low[u]=time++;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        if(!vis[v]) dfs(v,u);
        low[u]=min(low[u],low[v]);
    }
}

void  tarjan()
{
  time=0;
  memset(low,-1,sizeof(low));
  dfs(1,1);
}

int cost[maxn][maxn];
int d[maxn];
bool used[maxn];
int tree[maxn];

int  spfa( int s )//用处理单源最短路径,但是还要记录用上的边。
{
  int i,now;
  for( i=1;i<=n;++i )
  {
    d[i]=inf;
    used[i]=false;
  }
  d[s]=0;
  queue <int> q;
  q.push(s);
  used[s] = true;
  while(!q.empty())
  {
    now = q.front();
    q.pop();
    used[now] = false;
    for(i = 1; i <= n; i++)
    {
      if(d[i] > d[now] + cost[now][i])///这里的话算是用上这条边了么。。。
      {
        flag[s][now][i]=true;
        d[i] = d[now] + cost[now][i];
        if(used[i] == 0)
        {
          q.push(i);
          used[i] = true;
        }
      }
    }
  }
   int all=0;
   for(int i=1;i<=n;i++)
    all+=d[i];
   return all;
}

int  spfa2( int s )//用处理单源最短路径,但是还要记录用上的边。
{
  int i,now;
  for( i=1;i<=n;++i )
  {
    d[i]=inf;
    used[i]=false;
  }
  d[s]=0;
  queue <int> q;
  q.push(s);
  used[s] = true;
  while(!q.empty())
  {
    now = q.front();
    q.pop();
    used[now] = false;
    for(i = 1; i <= n; i++)
    {
      if(d[i] > d[now] + cost[now][i])///这里的话算是用上这条边了么。。。
      {
        d[i] = d[now] + cost[now][i];
        if(used[i] == 0)
        {
          q.push(i);
          used[i] = true;
        }
      }
    }
  }
   int all=0;
   for(int i=1;i<=n;i++)
    all+=d[i];
   return all;
}


struct node
{
    int u,v;
}E[maxm];


bool ans[maxm];
bool index[maxm];

void init()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        cost[i][j]=inf;
    for(int i=1;i<=n;i++)
        cost[i][i]=0;
}

void build()
{
    for(int i=1;i<=m;i++)
    {
        int x=E[i].u;
        int y=E[i].v;
         cost[x][y]=cost[y][x]=1;
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        memset(flag,false,sizeof(flag));
        memset(head,-1,sizeof(head));
        memset(vis,false,sizeof(vis));
        cnt=0;
        memset(index,false,sizeof(index));
        memset(g,0,sizeof(g));
        int tempa,tempb;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&tempa,&tempb);
            g[tempa][tempb]++;
            g[tempb][tempa]++;
            addedge(tempa,tempb);
            addedge(tempb,tempa);
            cost[tempa][tempb]=cost[tempb][tempa]=1;
            E[i].u=tempa;
            E[i].v=tempb;
        }
        for(int i=1;i<=m;i++)
        {

            int x=E[i].u;
            int y=E[i].v;
            if(g[x][y]>1)
                index[i]=true;
        }
        int weight=0;
        for(int i=1;i<=n;i++)
        {
           tree[i]=spfa(i);
           weight+=tree[i];
        }
        if(weight>=inf)
        {
            for(int i=1;i<=m;i++)
                puts("INF");
            continue;
        }
        tarjan();
        for(int i=0;i<cnt;i++)
        {
            int x=edge[i].from;
            int y=edge[i].to;
            if(low[x]!=low[y])
                edge[i].sign=true;
        }
        memset(ans,false,sizeof(ans));
        for(int i=0;i<cnt;i++)
        {
            if(edge[i].sign)
            {
                int temp=i/2+1;
                if(!index[temp])
                    ans[temp]=true;
            }
        }
         for(int i=1;i<=m;i++)
        {
//            if(ans[i])//桥的处理去掉就过了,应该是桥的处理的问题。
//             {
//                   puts("INF");continue;
//             }
             if(index[i])
             {
                 printf("%d\n",weight); continue;
             }
            int x=E[i].u;
            int y=E[i].v;
            init();
            build();
            cost[x][y]=cost[y][x]=inf;///建边
            int rees=0;
            for(int j=1;j<=n;j++)
            {
                if(flag[j][x][y]||flag[j][y][x])
                {
                    int temp=spfa2(j);
                    rees+=temp;
                }
                else
                    rees+=tree[j];
            }
            if(rees>=inf)
               puts("INF");
            else
               printf("%d\n",rees);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值