HDU 1003 Max Sum & Open_OJ NOI 1481 Maximum Sum

HDU 1003 Max Sum & Open_OJ NOI 1481 Maximum Sum

HDU 1003 Max Sum

题目链接 HDU 1003 Max Sum

思路 :

  • 线性 dp 求最大连续和 , f [ i ] f[i] f[i] 表示 1 ∼ i 1\sim i 1i 中最大子段和且子段中包含第 i i i 个元素
  • f [ i ] = m a x ( f [ i − 1 ] + a [ i ] , a [ i ] ) f[i]=max(f[i-1]+a[i],a[i]) f[i]=max(f[i1]+a[i]a[i]) ,划分依据 : 是否将 a [ i ] a[i] a[i] 并入连续子段

一些细节 :

根题题意 : 我们还要求最大子段和的左右端点 ,右端点即为最大值第一次出现的位置 ,左端点预处理一个前缀和来求即可 ,根据题意 ,左端点尽量靠左 。

ac 代码 :

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(int i = a;i <= b;i ++)
typedef long long ll;
typedef double db;

int n;
int const N = 1e5 + 10;
int s[N], w[N], f[N];

void solve(int i){
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> w[i], s[i] = s[i-1] + w[i]; // 预处理
    for(int i = 1; i <= n; i ++)
        f[i] = max(f[i-1] + w[i], w[i]); // Dp 转移
    int res = w[1], l, r = 1;
    for(int i = 2; i <= n; i ++) // 求最大值和右端点
        if(f[i] > res) res = f[i], r = i;
    for(l = 1; l <= r; l ++){ // 求左端点
        if(s[r] - s[l-1] == res){
            break;
        }
    }
    cout << "Case "<< i << ":\n";
    cout << res << ' ' << l << ' ' << r << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    for(int i = 1; i <= T; i ++){
        solve(i);
    }    
    
    return 0;
} 

题目链接 Open_OJ NOI 1481 Maximum Sum

思路 :

  • 本题与上一题基本类似 ,这题要求两端不相交的最大子段和
  • 我们先正着求一遍最大子段和 fl[i] : 表示 1 ∼ i 1\sim i 1i 中以 w [ i ] w[i] w[i] 结尾的最大子段和
    同理 ,倒着求一遍最大子段和 fr[i] : 表示 n ∼ i n\sim i ni 中以 w [ i ] w[i] w[i] 结尾的最大子段和
  • 然后在处理上面两个数组 ,首先使 fl[i] 表示 1 ∼ i 1\sim i 1i 中以任意 a[1~i] 元素结尾的最大子段和 ,fr[i] 表示 n ∼ i n\sim i ni 中以任意元素a[i~n] 结尾的最大子段和 。
  • 然后我们枚举两个子段的分界点 ,更新答案即可 。

ac 代码 :

#include<bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(int i = a;i <= b;i ++)
typedef long long ll;
typedef double db;

int n;
int const N = 1e5 + 10;
int w[N], fl[N], fr[N];

void solve(){
	memset(fl, -0x3f, sizeof fl);
	memset(fr, -0x3f, sizeof fr);
	cin >> n;
	for(int i = 1; i <= n; i ++) 
		cin >> w[i];
	
	// 第一遍处理
	for(int i = 1; i <= n; i ++) fl[i] = max(fl[i-1]+w[i], w[i]);
	for(int i = n; i >= 1; i --) fr[i] = max(fr[i+1]+w[i], w[i]);
	
	// 第二遍处理
	for(int i = 2; i <= n; i ++) fl[i] = max(fl[i], fl[i-1]);
	for(int i = n - 1; i >= 1; i --) fr[i] = max(fr[i], fr[i+1]);
	
	// 更新答案
	int res = INT_MIN;
	for(int i = 1; i < n; i ++)
		res = max(res, fl[i] + fr[i+1]);
	cout << res << '\n';
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int T = 1;
	cin >> T;
	for(int i = 1; i <= T; i ++){
		solve();
	}	
	
	return 0;
} 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值