【蓝桥杯专题】 DP(C++ | 洛谷 | acwing | 蓝桥)

菜狗现在才开始备战蓝桥杯QAQ


在这里插入图片描述

在这里插入图片描述

编程实现动态规划的状态转移方程时, 务必分清楚阶段、 状态与决策, 三者应该按照由外向内的顺序依次循环!! ————蓝书

背包问题

01背包

链接 链接
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 1010;
int f[N][N];
int v[N], w[N];

int main () {
    
    
    int n, V;
    cin >> n >>V;
    for(int i = 1; i <= n; i++) {
    
    
        cin >> v[i] >> w[i];
    }
    f[0][0] = 0;
    for (int i = 1; i <= n; i ++ ) {
    
    
        for(int j = 0; j <= V; j ++) {
    
     
            f[i][j] = f[i - 1][j]; // 不选
            if(v[i] <= j) {
    
     //如果选上的话 就在上一次加上 w[i]
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);            }
        }
    }
    cout << f[n][V] << endl;
    
    return 0;
}

  • 01背包优化 :
  • f[j]就是所有物品背包容量 j 下的最大价值。即一维f[j]等价于二维f[n][j]
  • 为什么一维情况下枚举背包容量需要逆序?在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N];
int v[N], w[N];

int main () {
    
    
    int n, V;
    cin >> n >>V;
    for(int i = 1; i <= n; i++) {
    
    
        cin >> v[i] >> w[i];
    }
    f[0] = 0;
    for (int i = 1; i <= n; i ++ ) {
    
    
        for(int j = V; j >= v[i]; j --) {
    
     
            // f[i][j] = f[i - 1][j]; // 不选
             //如果选上的话 就在上一次加上 w[i]
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[V] << endl;
	return 0;
}

AcWing 3. 完全背包问题

https://www.acwing.com/problem/content/3/

  • 朴素做法 : (TLE)
#include<iostream>
using namespace std;

const int N = 1010;

int n, m;
int dp[N][N], v[N], w[N];

int main(){
    
    
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ )
        cin >> v[i] >> w[i];

    for(int i = 1; i <= n; i ++ )
        for(int j = 0; j <= m; j ++ ) // 为什么从0 开始是因为不选的时候 容量就是 0 开始
            for(int k = 0; k * v[i] <= j; k ++ ) 
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
    cout << dp[n][m] << endl;
}
  • 根据错位相减法可得 : 实际上,我们在计算状态方程时不必多一个循环去单独枚举选择第i 个物品个数

for(int i = 1; i <= n; i++)
        for(int j = 0; j <= m; j++) 
        {
    
    
            f[i][j] = f[i - 1][j];  // 不选第i个物品
            if(j >= v[i])           // 可以选择第i个物品,状态方程见上面推导    
                f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]);
        }

//优化一维得到
 for(int i = 1; i <= n; i++)
        for(int j = v[i]; j <= m; j++) {
    
    
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }		

3382. 整数拆分 (完全背包)

题目 链接

  • 一个整数总可以拆分为 2 的幂的和。 (直接枚举 所有的2的次幂数)
  • 完全背包问题 直接删去一维即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
const int mod = 1e9;
// 从i个物品中选 , 且总体积恰好为j 的所有方案的集合
// 需不需要初始化  完全根据定义来写!!


int f[21][N]; 
//二维写法 会tle
int main () {
    
    
    int n = 0, m;
    cin >> m;
    f[0][0] = 1; // 去掉一维 
    for(int i = 1, v = 1; v <= m; i ++, v *= 2) {
    
     
        // 去掉 i  枚举 v
        n ++;
        for(int j = 0; j <= m; j ++) {
    
    
            f[i][j] = f[i - 1][j];  // f[j] == f[j]  直接去掉
            if(j >= v) f[i][j] = (f[i][j] + f[i][j - v]) % mod;
            // 直接把 v 搞到 j = v 就行
            //  完全背包问题  直接删去一维即可
            // 枚举顺序 : 物品  -》 容量 ——》 决策
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

// int f[N];
// int main ( ) {
    
    
//     int n;
//     cin >> n;
//     f[0] = 1; // 初始化不选的情况 
    
//     for(int i = 1; i <= n; i *= 2) {
    
    
//         for(int j = i; j <= n; j ++) {
    
    
//             f[j] = (f[j] + f[j - i]) % mod;
//         }
//     }
//     cout << f[n] << endl;    
    
//     return 0;
// }

1371. 货币系统(完全背包)

https://www.acwing.com/problem/content/description/1373/

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, V = 30;

long long f[V][N];
int w[N];

int main () {
    
    
    int v, n;
    cin >> v >> n;
    for (int i = 1; i <= v; i ++ ) cin >> w[i];
    f[0][0] = 1;
    for(int i = 1; i <= v; i ++) {
    
    
        for(int j = 0; j <= n; j ++) {
    
    
            for(int k = 0; k * w[i] <= j; k ++) {
    
    
                f[i][j] += f[i - 1][j - w[i] * k];
            }
        }
    }
    cout << f[v][n] << endl;
    return 0;
}


4. 多重背包问题 I

4877. 最大价值 (多重背包)

#include <iostream>
#include <queue>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define endl "\n"
const int N = 1000 + 10;
const int M =20;
const int INF = 0x3f3f3f;
const int mod = 1e9 + 7; 
typedef pair<int, int> pii;
typedef double db;
typedef long long ll; 
#define x first
#define y second
int f[N]; // 编号  容量
//int l[M], h[M], v[M], w[M], g[N];

// 多重背包 

void solve() {
    
    
	int n, m;
	int v, w;
	cin >> n >> m >> v >> w;
	// 初始化   因为0 是必须要选上的! 
	for(int i = v; i <= n; i ++)  f[i] = f[i - v]  + w; // 完全背包 
	
	
	for(int i = 1; i <= m; i ++) {
    
     // 01
        int l , h;
        cin >> l >> h >> v >> w;
	    for(int j = n; j >= v; j --) {
    
     // 完全背包 体积从大到小枚举 
            for(int k = 1; k <= l / h && k * v <= j; k ++) {
    
     // 01
                f[j] = max(f[j], f[j - v * k] + w * k);
            }
	    }
	}
	
	cout <<f[n] << endl;
}

int main () {
    
    
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//	freopen("input.txt", "r", stdin);
	int P = 1;
//	cin >> p
	while (P -- ) {
    
    
		solve();
	}	 	
	return 0;
}

线性DP

272. 最长公共上升子序列

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3010;
int n;
int a[N], b[N];
int f[N][N];

int main() {
    
    
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ ) cin >> b[i];
    for(int i = 1; i <= n; i ++ ) {
    
    
        int maxx = 1; // 保存 f(i - 1, j) 中 决策的最大值 !! 
        for(int j = 1; j <= n; j ++) {
    
    
            f[i][j] = f[i - 1][j];
            if(a[i] == b[j]) f[i][j]= max(maxx ,f[i][j]); // 更新
            if(b[j] < a[i]) maxx = max(maxx, f[i - 1][j] + 1); // 更新决策集合
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
    cout << res << endl;
    return 0;
}

271. 杨老师的照相排列

链接 链接
在这里插入图片描述


#include <bits/stdc++.h>
// #include <iostream>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define per(i, a, n) for(int i = n; i <= a; i --)
#define pb push_back;
#define fs first;
#define sz second;
#include <stdlib.h> // atoi
#define debug cout<<"debug"<<"\n"
#define endl "\n";
const int INF = 0x3f3f3f3f;
const int mod=1e9+7;
const int N = 31;
ll f[N][N][N][N][N];


void solve () {
    
    
    int n;
    while(cin >> n , n) {
    
    
        int s[5] = {
    
    0};
        rep(i, 0, n - 1) cin >> s[i];
        memset(f, 0 , sizeof f);
        f[0][0][0][0][0] = 1;   
        
        rep(a, 0, s[0]) {
    
    
            rep(b, 0, min(s[1], a)) {
    
    
                rep(c, 0, min(s[2], b)) {
    
    
                    rep(d, 0, min(s[3], c)) {
    
    
                        rep(e, 0, min(s[4], d)) {
    
    
// f[a][b][c][d][e]代表从后往前每排人数分别为a, b, c, d, e的所有方案的集合, 其中a >= b >= c >= d >= e; 逆序的
// f[a][b][c][d][e]的值是该集合中元素的数量;
                            ll &x = f[a][b][c][d][e];
                            if (a && a - 1 >= b) x += f[a - 1][b][c][d][e];
                            if (b && b - 1 >= c) x += f[a][b - 1][c][d][e];
                            if (c && c - 1 >= d) x += f[a][b][c - 1][d][e];
                            if (d && d - 1 >= e) x += f[a][b][c][d - 1][e];
                            if (e) x += f[a][b][c][d][e - 1];
// 当a > 0 && a - 1 >= b时,最后一个同学可能被安排在第1排,方案数是f[a - 1][b][c][d][e];
// 当b > 0 && b - 1 >= c时,最后一个同学可能被安排在第2排,方案数是f[a][b - 1][c][d][e];
// 当c > 0 && c - 1 >= d时,最后一个同学可能被安排在第3排,方案数是f[a][b][c - 1][d][e];
// 当d > 0 && d - 1 >= e时,最后一个同学可能被安排在第4排,方案数是f[a][b][c][d - 1][e];
// 当e > 0时,最后一个同学可能被安排在第5排,方案数是f[a][b][c][d][e - 1];
                        } 
                    }
                }
            }
        }
        cout << f[s[0]][s[1]][s[2]][s[3]][s[4]] << endl;
    }
}

int main(void) {
    
    
	freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T = 1;
    // cin >> T;
	while(T --) solve();
	return 0;
}

最长公共上升子序列

链接 链接

#include <bits/stdc++.h>
// #include <iostream>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define per(i, a, n) for(int i = n; i <= a; i --)
#define pb push_back;
#define fs first;
#define sz second;
#include <stdlib.h> // atoi
#define debug cout<<"debug"<<"\n"
#define endl "\n";
const int INF = 0x3f3f3f3f;
const int mod=1e9+7;
const int N = 3111;

int a[N], b[N];
int f[N][N];

void solve () {
    
    
    int n ;
    cin >> n;
    rep(i, 1, n) cin>> a[i];
    rep(i, 1, n) cin>> b[i];
    
    //版本1
    // rep(i, 1, n) {
    
    
    //     rep(j, 1 , n) {
    
    
    //         f[i][j] = f[i - 1][j];
    //         if(a[i] == b[j]) {
    
    
    //             // int maxv = 1;  // O(n^ 3)
    //             // for(int k =1; k < j; k ++) {
    
    
    //             //     if(b[j] > b[k]) {
    
    
    //             //         maxv = max(maxv, f[i - 1][k] + 1);
    //             //     }
    //             //     f[i][j] = max(maxv, f[i][j]);
    //             // }
    //         }
    //     }
    // }


    //版本2: 
    rep(i, 1, n) {
    
    
        int maxv = 1;
        rep(j, 1, n) {
    
    
            f[i][j] = f[i - 1][j];
            if(a[i] == b[j] ) f[i][j] = max(maxv, f[i][j]);
            if(a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
        }
    }
    int res = 0;
    rep(i, 1, n) {
    
    
        res = max(res, f[n][i]);
    }
    cout << res << endl;
}

int main(void){
    
    
	freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T = 1;
    // cin >> T;
	while(T --) solve();
	return 0;
}

鸣人的影分身 数字划分

链接 链接

在这里插入图片描述

  • 写dp 边界 和 循环 条件 都要注意!!
    在这里插入图片描述
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;


int main () {
    
       
    int t;
    cin >> t;
    while(t --) {
    
    
        int n, m; // n 个数     m 总和
        cin >> n >> m;
        int f[N][N] = {
    
    0};
        f[0][0] = 1;
        for(int i = 0; i <= n; i ++) {
    
    //枚举和
            for(int j = 1; j <= m; j ++) {
    
    //枚举个数
                f[i][j] = f[i][j - 1]; // 最小值为 0   
                if(i >= j) f[i][j] += f[i - j][j]; // 最小值为1 
             }
        }
        cout << f[n][m] << endl;
    }

    return 0;
}

P、糖果

链接 链接

  • 动态规划 ---- 闫式DP分析法

状态表示: f[i][j]
集合:从i个数中选总和模k为j的方案
属性:最大值

状态计算: ---- 集合的划分
划分依据:是否包含第i个数
1.包含第i个数 ---- f[i - 1, j - w % k] + w
2.不包含第i个数 ---- f[i - 1][j]
在这里插入图片描述

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 110;

int n, k;
int f[N][N];//从i个数中选总数模k为j的方案中的最大值

int main(){
    
    
    scanf("%d%d", &n, &k);
    memset(f, -0x3f, sizeof f);//f[0][1]、f[0][2]···无意义
    f[0][0] = 0;//本题求的是最大值而不是方案数,因此为0
    for(int i = 1; i <= n; i ++){
    
    
        int w;
        scanf("%d", &w);
        for(int j = 0; j <= k; j ++){
    
    
            f[i][j] = f[i - 1][j];
            f[i][j] = max(f[i][j], f[i - 1][(j - w % k + k ) % k] + w); 
            //							 	j - w % k >> (j - w % k + k ) % k //反正负数越界
        }
    }
    printf("%d\n", f[n][0]);
    return 0;
}

区间DP

AcWing 1222. 密码脱落 (区间dp)

链接 链接
在这里插入图片描述

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1100;

char s[N];
int f[N][N];//从i个数中选总数模k为j的方案中的最大值

int main(){
    
    
    scanf("%s", s);
    int n = strlen(s);
    
    for(int len = 1; len <= n; len ++) {
    
    
        for(int l = 0; l + len - 1 < n; l ++) {
    
    
            int r = l + len - 1;
            if(len == 1) f[l][r] = 1;
            else {
    
    
                if(s[l] == s[r]) f[l][r] = f[l + 1][r - 1] + 2;
                if(f[l + 1][r] > f[l][r]) f[l][r] = f[l + 1][r];
                if(f[l][r - 1] > f[l][r]) f[l][r] = f[l][r - 1];
            }
        }
    }
    cout << n - f[0][n - 1] << endl;
    
    // printf("%d\n", f[n][0]);

    return 0;
}

石子合并 (区间dp)

链接 链接
在这里插入图片描述

  • 区间dp模板题 : 区间抉择 f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
  • 注意 区间大小从2开始 以为1就是单个石子,没有意义 需要初始化
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int s[N];
int f[N][N];
int main () {
    
    
    cin >> n;
    memset(f, 0x3f, sizeof f); // 初始化
    for (int i = 1; i <= n; i ++ ) {
    
    
        f[i][i] = 0; // 初始化每个边界
        cin >> s[i];
        s[i] += s[i - 1];
    }
    
    for(int len = 2; len <= n; len ++) {
    
    
        for (int l = 1; l <= n - len + 1; l ++ ) {
    
    
            int r = l + len - 1;
            for(int k = l; k < r; k ++) {
    
    
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
            }
        }
    }
    
    cout << f[1][n] << endl;
    
    
    return 0;
}

AcWing 1070. 括号配对(区间dp)

题目链接
Snipaste_2023-03-27_11-55-28.png

思路

  • 闫式dp
  • 集合划分 f[l][r] 区间选择字母的所有方案
  • 属性 min
  • 集合划分 (先抉择 再合并):
  • (抉择划分) f[i][j] = min(f[i + 1][r - 1] , min (f[i + 1][r], f[i][r - 1] ) + 1)
  • (合并划分) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int INF = 0x3f3f3f;
const int N =  120;

string s;
int f[N][N];

bool match(int l, int r) {
    
    
    if(s[l] == '(' && s[r] == ')') return true;
    if(s[l] == '[' && s[r] == ']') return true;
    return false;
}

int main ( ) {
    
    
    cin >> s;
    int n = s.size();
    
    for(int i = 0; i < n; i ++) {
    
    
        f[i][i] = 1; // 初始化单个字母的区间
    }
    
    for(int len = 2; len <= n; len ++) {
    
    
        for(int l = 0; l + len - 1 < n; l ++) {
    
    
            int r = l + len - 1;
            f[l][r] = INF; // 初始化区间
            if(match(l, r)) f[l][r] = f[l + 1][r - 1]; // 如果匹配  则区间缩减 1
            f[l][r] = min(f[l][r], min(f[l + 1][r], f[l][r - 1]) + 1);  // 抉择 取最优方案
            
            for(int k = l; k < r; k ++) {
    
     // 抉择合并方案
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
            }
        }
    }
    cout << f[0][n - 1] << endl;
	return 0;
}

树形DP

没有上司的舞会 (树形dp)

链接
在这里插入图片描述
在这里插入图片描述

  • 思路
    状态表示
    在这里插入图片描述
    状态计算:f是s字节的子结点
  • 不选:f[u][0] += max(f[j][0], f[j][1])
  • 选:f[u][1] += f[j][1]
    在这里插入图片描述
  • C++代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include <string>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,  int> PII;
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define per(i, a, n) for(int i = n; i <= a; i --)
#define pb push_back
#include <stdlib.h> // atoi
#define debug cout<<"debug"<<"\n"
#define endl "\n"
const int INF = 0x3f3f3f3f;
const int mod=1e9+7;
const int N  = 6010;
#define x first
#define y second

int n;
int happy[N];
int h[N], ne[N], idx, e[N];
int f[N][2];
bool has_father[N];

void add (int a, int b) {
    
     // 把b 加到a
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u) {
    
    
    f[u][1] = happy[u]; // 选了
    for(int i = h[u]; i != -1; i = ne[i] ) {
    
     // 遍历树
        int j = e[i];
        dfs(j); // 递归下一层
        f[u][0] += max(f[j][0], f[j][1]); // 不选u  则可以选或者不选  取最优解即可
        f[u][1] += f[j][0]; // 选u   则他的子节点都不选
    }
}

void solve () {
    
    
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
    
    
        scanf("%d", &happy[i]);
    }
    memset(h, -1, sizeof h);
    for(int i = 0; i < n - 1; i ++) {
    
    
        int a, b;
        scanf("%d%d", &a, &b);
        has_father[a] = true; // 说明a 有直接上司
        add(b, a);
    }
    int root = 1; // 统计根节点个数
    while(has_father[root]) {
    
    
        root ++;
    }
    dfs(root);
    printf("%d\n", max(f[root][0], f[root][1]));
}

int main(void){
    
    
// 	freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T = 1;
    // cin >> T;
	while(T --) solve();
	return 0;
}

AcWing 1220. 生命之树 (树形dp)

在这里插入图片描述


#include<iostream>
#include<cstring>
#include<cstdio>
#include <limits.h>
#include<algorithm>
#include <string>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,  int> PII;
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define per(i, a, n) for(int i = n; i <= a; i --)
#define pb push_back
#include <stdlib.h> // atoi
#define debug cout<<"debug"<<"\n"
#define endl "\n"
const int INF = 0x3f3f3f3f;
const int mod=1e9+7;
const int N  = 100010;
const int M = N * 2;
#define x first
#define y second


// 上帝要在这棵树内选出一个非空节点集 S,使得对于 S 中的任意两个点 a,b,都存在一个点列 {a,v1,v2,…,vk,b} 使得这个点列中的每个点都是 S 里面的元素,且序列中相邻两个点间有一条边相连。
// 在这个前提下,上帝要使得 S 中的点所对应的整数的和尽量大。 === 实则就是求连通块的max

int n;
int w[N];
int h[N], ne[M], e[M], idx;
ll f[N];

void add(int a, int b) {
    
    
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int father) {
    
     // 记录父节点 防止往回走
    f[u] = w[u];
    for(int i = h[u]; i != -1; i  = ne[i]) {
    
    
        int j = e[i];
        if(j != father) {
    
    
            dfs(j, u);
            f[u] += max(0ll, f[j]); // long long的0,和0比较一下,如果<=0没必要加上
        }
    }
}

void solve () {
    
    
	cin >> n;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= n; i ++) cin >> w[i];
    for(int i = 0; i < n - 1; i ++) {
    
    
        int a, b;
        cin >> a >> b;
        add (a, b) , add(b, a);
    }
    dfs(1, -1); // 跟节点没有父节点 所以第二个参数为 -1
    ll res = f[1];
    for(int i = 1; i <= n; i ++) res = max(res, f[i]);
    cout << res << endl;
}

int main(void){
    
    
	// freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T = 1;
    // cin >> T;
	while(T --) solve();
	return 0;
}

最大的和 (前后缀分解)

题目链接

在这里插入图片描述

在这里插入图片描述

  • 分别求前后缀连续序列的值 最后再比较即可

思路 :

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50000 + 10;
const int INF = 1e9;
int n;

int w[N], f[N], g[N], h[N]; 
// g 最长连续前缀和   h : 最长连续后缀和

int main () {
    
    
    int t;
    scanf("%d", &t);
    while(t --) {
    
    
        n = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
        f[0] = g[0] = -INF;     // 边界
        for (int i = 1; i <= n; i ++ ) {
    
    
            f[i] = max(f[i - 1], 0) + w[i];
            g[i] = max(g[i - 1], f[i]);
        }
        h[n + 1] = f[n + 1] = -INF;
        for(int i = n; i ; i --) {
    
    
            f[i] = max(f[i + 1], 0) + w[i];
            h[i] = max(h[i + 1], f[i]); 
        }
        int res = -INF;
        for (int i = 1; i <= n; i ++ ) {
    
    
            res = max(res, g[i] + h[i + 1]);
        }
        cout << res <<endl;
    }
    return 0;
}

同类习题 : acwing3652

  • https://www.acwing.com/problem/content/description/484/
  • https://www.luogu.com.cn/problem/P3143

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_49486457/article/details/129448671