洛谷P11963 [GESP202503 六级] 环线
前言
一开始做这道时,感觉very简单,有手就会,结果后面感觉到难了(难道我没手?)。然后(小汪同学^ _ ^撤回了一条消息)。
另:不懂最大字段和的可以先做这道题。
解法
一开始我是用枚举起点的方法做的,喜提 \(40pts\)。
随后我又突发奇想,断环成链,再控制长度,是不是就能找到最优解呢?
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int n, a[400010] = { 0 }, ans = -1e9, dp[400010] = { 0 };
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i], a[n + i] = a[i];
int pos = 1;
for(int i = 1; i <= 2 * n; i++){
int x = a[i], y = dp[i - 1] + a[i];
if(x > y || pos == i - n){
dp[i] = x;
pos = i;
} else dp[i] = y;
ans = fmax(ans, dp[i]);
}
cout << ans;
}
交上去一看,\(80pts\)。
然后开始思考正解。
首先行驶情况分为两种:
1.不跨环
就相当于直接求最大子段和
2.跨环
这个时候肯定不能枚举,那怎么办呢?
别急,先画个图:

这样可以先求最小子段和,再用总和减去它。
于是 \(100pts\) 代码就出来了:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MIN = -1e9 - 1;
int n, a[200010] = { 0 }, max1 = MIN, max2 = 0, maxn = LLONG_MIN;
int dp[200010] = { 0 }, f = 0;
int Max(int x, int y){
if(x > y) return x;
else return y;
}
int Min(int x, int y){
if(x > y) return y;
else return x;
}
signed main(){
cin >> n;
int sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
maxn = Max(maxn, a[i]);
if(a[i] < 0) f++;
}
//最大子段和
for(int i = 1; i <= n; i++){
dp[i] = Max(a[i], dp[i - 1] + a[i]);
max1 = Max(max1, dp[i]);
}
//最小子段和
int minn = LLONG_MAX;
memset(dp, LLONG_MAX, sizeof(dp));
dp[0] = 0;
for(int i = 1; i <= n; i++){
dp[i] = Min(a[i], dp[i - 1] + a[i]);
minn = Min(minn, dp[i]);
}
max2 = sum - minn;
int t = Max(max1, max2);
if(f == n) cout << maxn;
else cout << t;
return 0;
}
后知后觉
原来我代码里的\(Max\)函数和\(Min\)函数可以换成\(fmax\)和\(fmin\),我TM真傻!!!

浙公网安备 33010602011771号