通过题目描述,我们得知如果枚举所有的天数,就不会通过所有的样例,因此我们可以通过二分来列举符合要求的天数,并且我们知道两个城市之间衡量的灰尘度标准就是灰尘度总和最小的那一段路径,也就是说我们需要寻找到权值和最低的那条路径,而我们知道每两个点之间都有路径可走,因此我们可以用弗洛伊德算法来寻找最小的灰尘度路径,而弗洛伊德算法在本题中应用就是以每个点为中间点,寻找两点直通和两点之间多走中间点的两条路径中最小灰尘度的那种情况,最后再进行P值计算,将P与Q的比较值作为二分算法的依据,需要注意的是,如果一直没有合适的答案,就输出-1,也就是说check函数一直返回false,我们可以用ans = -1来存储答案,当r更新的时候,将ans同步更新成r的值,因此如果没有满足要求的天数,自然就会输出-1。
上代码
#include<iostream>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
int n, Q;
const int N = 1e2 + 10;
int d[N][N];//i到j的灰尘度
int limit[N][N];//i到j的灰尘度下限
int tmp[N][N];//备份数组
bool check(int mid)
{
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
tmp[i][j] = d[i][j];
}
}//备份数组,防止修改原数组,导致下次使用时候发生变化
for(int i = 0; i < n; i++){
int val = mid / n;//获取走了val圈
if(mid % n >= i + 1) val++;
for(int j = 0; j < n; j++){
tmp[i][j] = max(tmp[i][j] - val * 2, limit[i][j]);//因为前一个点减少灰尘度,下一个点也要减少,因此减少两倍,并且不能小于limit[i][j]因此二者取最大值
}
}
for(int k = 0; k < n; k++){//以k为中间点,寻找最短路径(最小灰尘度)
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(tmp[i][j] > tmp[i][k] + tmp[k][j]){
tmp[i][j] = tmp[i][k] + tmp[k][j];
}//如果当前的灰尘度小于以k为中间点的路径的灰尘度,就更新当前灰尘度
}
}
}
int sum = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
sum += tmp[i][j];//求当前指标P
}
}
return sum <= Q;//如果当前指标P小于等于Q说明满足条件,反之就是不满足
}
signed main(void)
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> Q;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> d[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> limit[i][j];
}
}
int l = 0, r = 1e18;
int ans = -1;//因为可能会有一种情况导致无法达标,如果无法达标说明r无法更新,因此可以用ans取代l进行输出,如果一直不达标就会输出-1
while(l < r){
int mid = (l + r) / 2;
if(check(mid)){
r = mid;//如果当前mid已经满足条件,就将r更新
ans = r;//更新r同时更新ans
}
else l = mid + 1;//如果不满足条件,就将l更新
}
cout << ans << endl;
return 0;
}