hdu2433 Travel bfs+最短路树 (预处理)

该博客主要探讨了HDU2433 Travel问题的解决方法,强调在每条边长度为1的情况下,正确答案应基于最短路树的求和。作者指出,尽管SPFA和Dijkstra的代码可以得到AC,但它们可能不准确。正确解法是使用BFS预处理每点到其余点的最短路径和,同时记录前驱节点,以便于判断删除的边是否影响最短路树。对于重复边或不在最短路树上的边,删除操作不会改变答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你n个点,m条双向边,每条边的长度都为1,求删去第i条边 后, 每个点到其他点的最短路的距离之和.

//题意很不清晰

网上有最短路spfa 和djskra 的做法, 而且代码可以ac, 但是代码是错的,只是刚好卡着时间过了

由于每条边的长度都一样, 而且根据答案我们可以得出, 每一个点到其他点的距离之和 应该是一棵树,所以我们要求n棵最短路树的和,所以先预处理记录每个点为原点的最短路树之和, 所以正解应该是bfs + 最短路树

做法:

先对每一个点都进行一次bfs, 计算出每个点到其他点的距离之和, 如果无法bfs到所有的点说明不连通,那么可以直接打印INF;

在预处理时, 我们可以先做一个操作,在bfs的时候,对每一个点以某个点为原点进行bfs时候的前驱进行记录,方便 我们后面 进行判断要删除的边是否在 我们之前bfs过的最短路树上,

题目还会出现重边的情况, 那么如果删除的边是 重边的话,那么其实答案不会发生变化, 不用进行处理, 要删去的边如果没在最短路树上的话 也不用进行处理,答案没有变化。

/*
Algorithm:
Author: anthony1314
Creat Time:
Time Complexity:
*/

#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cstring>
#include<cstdio>
//#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define mod 1e9 + 7
#define line printf("--------------");
using namespace std;
#define inf 0x3f3f3f3f
int mapp[105][105];
bool vis[105];
int sum[3005];
int pre[105][105];
int dis[105];

struct node {
	int end;
	int first;
} edge[3005];
int n, m;
int bfs(int k, bool flag) {
	memset(vis, false, sizeof(vis));
	memset(dis, 0, sizeof(dis));
	int cnt = 1, summ = 0;
	queue<int> que;
	while(!que.empty()) {
		que.pop();
	}
	vis[k] = true;
	que.push(k);
	while(!que.empty()) {
		int s = que.front();
		que.pop();
		for(int i = 1; i <= n; i++) {
			if(!vis[i] && mapp[i][s] > 0) {
				if(flag) { // 记录第一次bfs时 每个点的前驱 方便后面进行判断
					pre[k][i] = s;//以k为源点 i的前驱为s
				}
				dis[i] = dis[s] + 1;
				summ += dis[i];
				vis[i] = true;
				que.push(i);
				cnt++;
			}
		}
	}
	if(cnt != n) {//访问的点不到n个说明不连通
		return -1;
	} else {
		return summ;
	}
}

int main() {
	while(~scanf("%d %d", &n, &m)) {
		memset(mapp, 0, sizeof(mapp));
		memset(pre, 0, sizeof(pre));
		memset(sum, 0, sizeof(sum));
		int x, y;
		for(int i = 0; i < m; i++) {
			scanf("%d %d", &x, &y);
			mapp[x][y]++;
			mapp[y][x]++;
			edge[i].first = x;
			edge[i].end = y;
		}
		int k;
		int summ = 0;
		for(k = 1; k <= n; k++) {
			int ans = bfs(k, true);
			if(ans == -1) {//没删边之前就不连通
				break;
			}
			sum[k] = ans;
			summ += ans;
		}
		if(k <= n) {
			for(int i = 0; i < m; i++) {
				puts("INF");
			}
			continue;
		}
		for(int i = 0; i < m; i++) {
			int x = edge[i].first, y = edge[i].end;
			if(mapp[x][y] > 1) {//重边 无影响
				printf("%d\n", summ);
				continue;
			}
			int sum1 = 0, s1;
			for(int j = 1; j <= n; j++) {
				if(pre[j][x] == y || pre[j][y] == x) { //如果 x-y 都在 第j颗bfs最短路树上  就需要重新进行计算
					mapp[x][y] = 0;
					mapp[y][x] = 0;//删除边
					s1 = bfs(j, false);
					if(s1 == -1) {
						mapp[x][y]++;
						mapp[y][x]++;//不连通
						sum1 = -1;
						break;
					}
					sum1 += s1;
					mapp[x][y]++;
					mapp[y][x]++;
				} else {
					sum1 += sum[j];
				}
			}
			if(sum1 == -1) { //不连通
				puts("INF");
			} else {
				cout<<sum1<<endl;
			}
		}
	}


	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值