洛谷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真傻!!!

posted @ 2026-06-03 20:09  小汪同学^_^  阅读(15)  评论(0)    收藏  举报