[题解]和

[题目描述]

给定一个长度为n的序列,你每次可以合并相邻两个元素,新的元素为这两个元素的和。你需要使得若干
次合并之后的序列非降,求最小合并次数。

[数据范围]

n<=1500

[题解]

      这道题的标程用的是n^2log(n)的dp,所以才会有奇葩的1500的范围.

      然而,这道题完全可以在O(n^2)的时间内解决.

      考虑前i个数的决策.如果我们要合并成合法序列后,最后的值最小,那么我们一定要使合并的次数最小.因为在如果合并次数最少且最后的值最小的情况下再进行合并,最后的值显然不会变大,即:不可能出现一种情况,使得合并的次数不是最少而最后面的值最小.因为我们一切的合并操作都是"被逼"的,所以多合并不可能使最后的数变大,于是上面的结论也可以很容易的想出来.

Code:

program sequence;
type int=longint;
var
        i,j,k,m,n:int;
        a,f,g,s:array[0..1500]of int;
begin
        assign(input,'sequence.in');reset(input);
        assign(output,'sequence.out');rewrite(output);
        read(n);
        for i:=1 to n do begin read(a[i]);s[i]:=s[i-1]+a[i];end;
        fillchar(f,sizeof(f),100);
        fillchar(g,sizeof(g),100);
        f[1]:=0;g[1]:=a[1];
        for i:=2 to n do begin
                for j:=1 to i-1 do begin
                        if(f[j]+i-j-1<=f[i])and(g[j]<=s[i]-s[j])then begin
                                g[i]:=s[i]-s[j];f[i]:=i-j-1+f[j];
                        end;
                end;
        end;
        write(f[n]);
        close(input);close(output);
end.

考试时因为前两题太水了,本以为第三题会搞一道难题防AK的,所以就随便打了个贪心,只搞到了10分.知道正解这么简单后简直想吐血= =.

有很多人觉得这样dp的正确性不好证,其实这样做的正确性是显然的,到时候我再将完整版题解搞上来.

//=================================================================================================

好了,考也考完了.现在我来讲一下这道题目的线性做法.


将方程变形得到:

F[i]=min(f[j]-j)+i-1,G[j]+s[j]<=s[i].

显然,F[j]-j随j单调不增,s[i]单调上升.

于是,我们维护这样一个单调队列,使得下标单调递增(即F[j]-j单调递减),G[j]+s[j]单调上升,之后怎么做应该都会了吧.


BY QW

转载请注明出处


参考资源链接:[C++解决LeetCode第86题:分隔链表题解](https://wenku.youkuaiyun.com/doc/2v3shkrvm5?utm_source=wenku_answer2doc_content) 在解决LeetCode第86题——分隔链表的过程中,掌握C++语言链表操作是关键。为了帮助你更好地理解并实现这一题目的解决方案,特别推荐《C++解决LeetCode第86题:分隔链表题解》这份资源。通过这份资料,你可以获得关于本题题解的详细解析代码实现,直接应对当前的编程挑战。 分隔链表题要求将链表中所有小于给定值x的节点移动到所有大于等于x的节点之前,同时保持原有节点的相对顺序。以下是使用C++实现的步骤代码示例: 1. 创建两个虚拟头节点,分别为smalllarge,这有助于处理边界条件,简化链表的插入操作。 2. 遍历原始链表,根据节点值与x的比较结果,将节点分别插入到smalllarge链表中。 3. 将small链表的最后一个节点连接到large链表的第一个节点,完成链表的分隔。 4. 返回small链表的头节点作为最终链表的头节点。 以下是C++代码实现: ```cpp // 定义链表节点结构 struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; // 分隔链表函实现 ListNode* partition(ListNode* head, int x) { ListNode dummySmall(0), dummyLarge(0); ListNode *small = &dummySmall, *large = &dummyLarge; while (head) { if (head->val < x) { small = small->next = head; } else { large = large->next = head; } head = head->next; } small->next = dummyLarge.next; large->next = nullptr; return dummySmall.next; } ``` 在掌握上述分隔链表的解法后,建议进一步深入学习链表操作的各种技巧,包括反转链表、合并链表、检测环等,这将对提升你的据结构算法水平大有裨益。为了继续扩展你的知识,不妨探索更多类似的C++题解算法实践资源,这会加深你对LeetCode平台以及编程面试准备的理解。 参考资源链接:[C++解决LeetCode第86题:分隔链表题解](https://wenku.youkuaiyun.com/doc/2v3shkrvm5?utm_source=wenku_answer2doc_content)
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值