HDU1024(最大子段和变式+动态数组)(明天重做)(过一段时间下次看到再重做)

本文探讨了HDU1024问题,即如何将数组分成m段以获得最大和,不允许段间相交。通过动态规划方法解决,其中d[i][j]表示前j个数字分成i组的最大和。文章提到了两种情况,并给出了状态转移方程。作者通过错误分析和重试加深了对问题的理解,但表示仍需进一步研究。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二天更新:pre[]数组相当于存的上一次(第一维是i-1)的状态中的最大值

https://vjudge.net/problem/HDU-1024#author=SDUProgramming
下次看到重新看看注释

把一个数组分成m段, sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im,
jm),求使得上述和最大,ik,jk是连续的jk和ik+1可以不连续
动态规划,d[i][j]表示在选取第j个数字的情况下,将前j个数字分成i组的最大和, 则它的值有两种可能
①(x1,y1),(x2,y2)…(xi,yi,num[j])
②(x1,y1),(x2,y2)…(xi-1,yi-1),…,(num[j]),其中yi-1是第k个数字
故:d[i][j]=max(d[i][j-1],d[i-1][k])+num[j],其中k=i-1,i,…,j-1
参考:https://blog.youkuaiyun.com/pmt123456/article/details/52695470

原题(hdu1024)

东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j) 问题是要找到sum(i1, j1) + … +
sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。 注:1
≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)

本来觉得自己比较聪明,然而发现还是太笨了。
实在做不出来,答案也好不容易看的有点懂了
只能笨鸟先飞,多分析点这种不会的题目,然后记住了
明天重做

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <vector>

using namespace std;

const int MAXN_try = 100 + 5;
const int MAXN = 1e6 + 5;
int n, m;
int dp_try[MAXN_try][MAXN_try]; //第j个人放在第i组时候的最大值
int dp[MAXN];
int a[MAXN];
int pre[MAXN];
int pre_try[MAXN_try][MAXN_try];
int k;
//未优化
void solve() {
    memset(dp_try, 0, sizeof(dp_try));
    for (int i = 1; i <= m; i++) { //区间数
        for (int j = i; j <= n; j++) {
            int temp = -0x3f3f3f3f;
            for (int k = i - 1; k <= j - 1; k++) {
                temp = max(temp, dp_try[i - 1][k]);
                //temp数据保存到数组pre[],用的时候直接用pre[j-1]
                //pre[j] = max(pre[j-1],dp_try[i-1][j])
            }
            dp_try[i][j] = max(temp + a[j], dp_try[i][j - 1] + a[j]); //dp_try[i][j] = max(temp + a[j], dp_try[i-1][j - 1] + a[j]);
        }
    }
}
//只使用pre_try[][]数组优化时间,未使用滚动数组优化空间
int solve_1() {
    int temp = -0x3f3f3f3f;
    memset(dp_try, 0, sizeof(dp_try));
    memset(pre_try, 0, sizeof(pre_try));
    for (int i = 1; i <= m; i++) {
        temp = -1e6; //
        for (int j = i; j <= n; j++) {
            dp_try[i][j] = max(dp_try[i][j - 1] + a[j], pre_try[i - 1][j - 1] + a[j]);
            // // pre_try[i - 1][j - 1]
            // 等同于 for (int k = i - 1; k <= j - 1; k++) {
            //  max(temp, dp_try[i - 1][k]);}
            pre_try[i][j - 1] = temp;
            temp = max(dp_try[i][j], temp);
        }
    }
    return temp;
}
//正式解
int solve_fast() {
    memset(dp, 0, sizeof(dp));
    memset(pre, 0, sizeof(pre)); //这里必须有
    int tmp = -0x3f3f3f3f;
    for (int i = 1; i <= m; i++) {
        //隐隐约约懂了。pre[]数组实际相当于一个二维数组pre[i][j]
        //第一维相当于利用滚动数组省略了(第一维一直等于i-1)
        tmp = -0x3f3f3f3f; //用-1e6会导致错误答案
        for (int j = i; j <= n; j++) {
            //酝酿不出来了
            //参考下https://blog.youkuaiyun.com/pmt123456/article/details/52695470借点思路
            //不过有个问题,pre[]不是维护的i-1到j-1范围的最小值?只有一维怎么维护状态?
            //沉思中……
            dp[j] = max(pre[j - 1] + a[j], dp[j - 1] + a[j]); //pre[j-1]相当于pre[i-1][j-1];
            pre[j - 1] = tmp;
            tmp = max(tmp, dp[j]);
            /*
            相当于:
            dp[i][j] = max(pre[i - 1][j - 1] + a[j], dp[i - 1][j - 1] + a[j]);
            pre[i][j - 1] = tmp; //必须放这里,实现滚动数组
            tmp = max(tmp,dp[i][j]);//相当于j个元素分成i个子段字段和的最大值
            */

            /*
            tmp = max(tmp, dp[j]);
            pre[j] = tmp;这么写存在问题
            */
        }
    }
    return tmp;
}
int main() {
#ifdef LOCAL
    freopen("zz_in.txt", "r", stdin);
    freopen("zz_op.txt", "w", stdout);
#endif
    while (~scanf("%d %d", &m, &n)) {
        // scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        // solve();
        // cout << dp_try[m][n] << endl;//这样写不对(最大解不一定包含最后一个元素)
        // cout << solve_1() << endl;
        cout << solve_fast() << endl;
        // cout << dp[n] << endl; //fault
    }
    //int t, i, j, k;
    //cin>>t;
    //while(t--)
    //{

    //}

#ifdef LOCAL
    printf("Time used = %.2f\n", (double) clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

第二天更新:
不够自信啊,return位置写错了导致WA(弱智bug),结果却怀疑自己dp写错了;编译器都有警告也没看到
关键还是理解的不算特别透彻啊
下次继续,mmp,不信了

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <vector>

using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 5;
int n, m;
int dp[MAXN];
int pre[MAXN];
int a[MAXN];
int solve() {
    int tmp = -INF;
    memset(dp, 0, sizeof(dp));
    memset(pre, 0, sizeof(pre));
    for (int i = 1; i <= m; i++) {
        tmp = -INF;
        for (int j = i; j <= n; j++) {
            dp[j] = max(dp[j - 1] + a[j], pre[j - 1] + a[j]);
            pre[j - 1] = tmp;
            tmp = max(tmp, dp[j]);
        }
    }
    return tmp; //return错了位置导致报错……
}
int main() {
#ifdef LOCAL
    freopen("zz_in.txt", "r", stdin);
    freopen("zz_op.txt", "w", stdout);
#endif
    while (~scanf("%d %d", &m, &n)) {
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        cout << solve() << endl;
    }

#ifdef LOCAL
    printf("Time used = %.2f\n", (double) clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值