zoukankan      html  css  js  c++  java
  • [题解] hdu 2433 Travel (BFS)

    - 传送门 -

     http://acm.hdu.edu.cn/showproblem.php?pid=2433
     

    # Travel

    Time Limit: 10000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 3246    Accepted Submission(s): 1112

    Problem Description

    One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will always take the shortest way. There are M (M <= 3000) two-way roads connecting the towns, and the length of the road is 1.
          Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.

    Input

    The input contains several test cases.
          The first line contains two positive integers N, M. The following M lines each contains two integers u, v, meaning there is a two-way road between town u and v. The roads are numbered from 1 to M according to the order of the input.
          The input will be terminated by EOF.

    Output

    Output M lines, the i-th line is the new SUM after the i-th road is destroyed. If the towns are not connected after the i-th road is destroyed, please output “INF” in the i-th line.

    Sample Input

    5 4
    5 1
    1 3
    3 2
    5 4
    2 2
    1 2
    1 2

    Sample Output

    INF
    INF
    INF
    INF
    2
    2

    Source

    2008 Asia Chengdu Regional Contest Online

    Statistic | Submit | Discuss | Note

    - 题意 -

     给出 n 个点, m 条边. (边权都为1)
     定义SUM[i] = i 到其它节点的最短距离之和.
     ans = SUM[1] + SUM[2] + ... + SUM[n].
     求在摧毁第x条边的情况下ans最小值.(1 <= x <= m)
     

    - 思路 -

     BFS.
     因为边权为1, 所以我们可以直接(从某点开始)搜索得到到达其它点的最短路.
     先对每个点处理出一棵树(表示其它节点到它的最短距离), 并记录每个点的这棵树包含了哪些边.
     处理每一条边时, 我们都要遍历每一棵树, 考虑这条边不在树上或者这条边有重边(断掉这条还有同样的边存在)时, 可以直接得到答案, 否则就要把边断掉, 重新BFS得到答案.
     
     细节见代码.
     
     PS :
     需要注意的几点:
     1. 拆边后要把边重新连起来.
     2. 预处理出每棵树表示的每个点到某点的距离和, 在拆边处理时不会更改, 以便下次处理.
     3. 蜜汁卡常...
     

    - 代码 -

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 100 + 5;
    const int M = 3e3 + 5;
    
    int DIS[N], VIS[N], SUM[N];
    int MAP[N][N], X[M], Y[M];
    int USED[N][N][N];
    int n, m, ans;
    queue<int>q;
    
    void init() {
    	memset(MAP, 0, sizeof (MAP));
    	memset(SUM, 0, sizeof (SUM));
    	memset(USED, 0, sizeof (USED));
    	ans = 0;
    }
    
    int BFS(int x, int flag) {
    	while (!q.empty())
    		q.pop();
    	memset(VIS, 0, sizeof (VIS));
    	memset(DIS, 0, sizeof (DIS));
    	q.push(x);
    	VIS[x] = 1;
    	while (!q.empty()) {
    		int u = q.front();
    		q.pop();
    		for (int i = 1; i <= n; ++i)
    			if (!VIS[i] && MAP[i][u]) { //就是这里!!!写反了导致我卡了巨久!!!为这种卡常姿势献上膝盖!!!
    				VIS[i] = 1;
    				if (flag) { USED[x][i][u] = 1; USED[x][u][i] = 1; } //表示找 x 到其它点的最短路中包括了 i - u 这条路
    				DIS[i] += DIS[u] + 1; //边权为 1
    				q.push(i);
    			}
    	}
    	int tot = 0;
    	for (int i = 1; i <= n; ++i) {
    		if (DIS[i] == 0 && i != x)
    			return -1;
    		tot += DIS[i];
    	}
    	return tot;
    }
    
    int main() {
    	while (~scanf("%d%d", &n, &m)) {
    		init();
    		for (int i = 1; i <= m; ++i) {
    			int x, y;
    			scanf("%d%d", &x, &y);
    			X[i] = x; Y[i] = y;
    			MAP[x][y] ++;
    			MAP[y][x] ++;
    		}
    		for (int i = 1; i <= n; ++i) {
    			SUM[i] = BFS(i, 1);
    			if (SUM[i] == -1) {
    				ans = -1;
    				break;
    			}
    			ans += SUM[i];
    		}
    		for (int i = 1; i <= m; ++i) {
    			MAP[X[i]][Y[i]] --;
    			MAP[Y[i]][X[i]] --;
    			int tot, tmp = ans;
    			if (tmp == -1)
    				printf("INF
    "); //图原本就不连通
    			else if (MAP[X[i]][Y[i]] > 0)
    				printf("%d
    ", ans); //重边
    			else {
    				for (int j = 1; j <= n; ++j)
    					if (USED[j][X[i]][Y[i]]) {
    						tot = BFS(j, 0);
    						if (tot == -1) {
    							printf("INF
    ");
    							break;
    						}
    						tmp += tot - SUM[j]; //边在树中则算拆边后的情况
    					} //边不在树中则不作处理
    				if (tot != -1) printf("%d
    ", tmp);
    			}
    			MAP[X[i]][Y[i]] ++;
    			MAP[Y[i]][X[i]] ++; //把边连回来
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    微信企业号开发:UserAgent
    用sinopia搭建内部npm服务
    python format用法详解
    python正则表达式re之compile函数解析
    Socket通信原理
    TCP半开连接与半闭连接
    使用npm安装一些包失败了的看过来(npm国内镜像介绍)
    UI优秀框架(库)
    关于 WebView 知识点的详解
    CommonJS规范
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7399661.html
Copyright © 2011-2022 走看看