【题目链接】
【题目考点】
1. 贪心
【解题思路】
1. 贪心选择性质的证明
贪心选择:每次从第1个未被选择的数字开始,选择尽量长的满足加和小于等于m的子段
证明:
证明:
记整个数字序列为数组a,a[i]
表示数字序列的第i个数字。
- 证明:存在最优解包含从第1个数字开始的尽量长的满足加和小于等于m的子段
假设按照上述方法选择的子段为a[1]~a[g]
,“子段尽量长”指的是如果把a[g+1]
加入到子段中,整个子段的加和就会大于m。因此选择的子段不能更长。
如果选择的第一个子段更短也可以得到最优解,使用反证法:
假设:最优解不包含贪心选择,存在最优解,第一个子段为a[1]~a[k]
,满足k<g。自然a[k+1]~a[g]
不属于第一个子段。
把a[k+1]~a[g]
从其它子段转到第一个子段,使第一个子段是贪心选择为a[1]~a[g]
。其它子段去掉a[k+1]~a[g]
的元素后,可能会使子段数量减少,可能子段中元素减少,那么该子段的加和减少或不变(因为每个元素都是非负数),不影响“每个子段加和小于等于m”的要求,子段数量不变。要求的是最少子段数量,当前已经是最优解,子段数量不变或减少,该分段仍然是最优解。该最优解包含了贪心选择,与假设相悖,原命题得证。- 证明:在前k次进行贪心选择后,证明最优解包含第k+1次的贪心选择。
证明方法同上,略。
2. 具体做法
顺序遍历数组,对于每个元素
- 如果当前子段在添加该元素后,子段和不超过m,将该元素添加进当前子段,子段和增加该元素值
- 否则该元素自己作为下一个子段的第一个元素,子段数量增加1。
最后输出子段数
【题解代码】
解法1:贪心
#include<bits/stdc++.h>
using namespace std;
#define N 100005
int n, m, a[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> a[i];
int sum = 0, ct = 1;
for(int i = 1; i <= n; ++i)
{
if(sum+a[i] <= m)
sum += a[i];
else
{
ct++;
sum = a[i];
}
}
cout << ct;
return 0;
}