【洛谷】P8794 [蓝桥杯 2022 国 A] 环境治理 的题解

【洛谷】P8794 [蓝桥杯 2022 国 A] 环境治理 的题解

题目传送门

思路

因为对于 P P P 的定义已经给出很明显的提示了——多源最短路径,用 floyd。

然后就是本题的图的特殊之处:灰尘度的变化。

这个特殊之处直接导致了想一次 floyd 直接解决问题是不可能的。

因为 floyd 得到的最短路径抽象去了路径上的点,那样就不知道哪条最短路径会缩短了。

去思考本题的答案会发现:

  1. 天数越多,就越可能达标。

  2. 本题要求的是最少需要多少天。

然后就明白了大概的解题思路:二分搜索天数,用 floyd 判断这天的灰尘度是否达标。

接下来就是一些细节的问题了,例如对于完全图用二维数组存图、每轮搜索之前都需要根据天数初始化图的边权。

由于道路改善发生的次数越多,指标 P P P 越小。故根据这种单调性,可以采用二分,二分道路改善次数 x x x。已知道路改善次数的情况,我们可以很快的知道图上每一条边当下的边权,然后我们可以用 floyd 算法计算一下指标 P P P,判断指标 P P P 是否小于等于 Q Q Q 即可,复杂度 O ( n 3 log ⁡ K ) O(n^3 \log K) O(n3logK),其中 K K K 为最大的道路改善次数,设成 1 0 9 10^9 109左右即可。

计算得到天数的范围是 0 0 0 1 0 7 10^7 107

以及最重要的,关于数据范围的问题 q q q 的最大值已经超出了 int 所能达到的精度,应该用 long long 存储。

注:即使是 long long,累加结束之后也可能溢出,故需要加上正数判断。

代码

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <climits>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <ctime>
#include <string>
#include <cstring>
#define lowbit(x) x & (-x)
#define endl "\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO {
	inline int read() {
		register int x = 0, f = 1;
		register char c = getchar();
		while (c < '0' || c > '9') {
			if(c == '-') f = -1;
			c = getchar();
		}
		while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
		return x * f;
	}
	inline void write(int x) {
		if(x < 0) putchar('-'), x = -x;
		if(x > 9) write(x / 10);
		putchar(x % 10 + '0');
		return;
	}
}
using namespace fastIO;
int w[105][105];
int ww[105][105];
int minn[105][105];
int n, q;
int check(int day) {
	int ans = 0;
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < n; j ++) {
			ww[i][j] = w[i][j];
		}
	}		
	for(int i = 0; i < n; i ++) {
		int val = day / n + (day % n >= i + 1 ? 1 : 0);
		for(int j = 0; j < n; j ++) {
			ww[i][j] -= val, ww[j][i] -= val;
		}	
	}
	for(int i = 0; i < n; i ++) {
		for (int j = 0; j < n; j ++) {
			ww[i][j] = max(minn[i][j], ww[i][j]);
		}
	}		
	for(int k = 0; k < n; k ++) {
		for (int i = 0; i < n; i ++) {
			for (int j = 0; j < n; j ++) {
				ww[i][j] = min(ww[i][j], ww[i][k] + ww[k][j]);
			}
		}
	}		
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < n; j ++) {
			ans += ww[i][j];
		}
	}
	return ans;
}
int main() {
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	n = read(), q = read();
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < n; j ++) {
			w[i][j] = read();
		}
	}		
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < n; j ++) {
			minn[i][j] = read();
		}
	}	
	int l = 0, r = 100000 * n, ans = -1;
	while(l <= r) {
		int mid = l + r >> 1;
		if(check(mid) <= q) {
			r = mid - 1;
			ans = mid;
		} 
		else {
			l = mid + 1;
		}
	}
	write(ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值