这道题是一道很巧妙的最短路。题目大致的意思就是给定一张无向图,枚举每条边删去,求剩下的图中每个
点对距离之和。
//
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);
}
}
}
HDU 2433 最短路
最新推荐文章于 2020-05-27 18:00:13 发布