信息学奥赛一本通 ybt 1243:月度开销 | OpenJudge NOI 1.11 06:月度开销 | 洛谷 P2884 [USACO07MAR] Monthly Expense S

【题目链接】

ybt 1243:月度开销
OpenJudge NOI 1.11 06:月度开销
洛谷 P2884 [USACO07MAR] Monthly Expense S

【题目考点】

1. 贪心
2. 二分答案

【解题思路】

约翰每天的开销为序列中的一个元素,每个fajo月就是序列中的一个子段。一个fajo月的开销就是子段和。
他要使开销最多的fajo月的开销尽可能少,求最大月度开销的最小值。
也就是求对序列的所有子段划分的方案中,最大子段和最小的方案的最大子段和。
解题方法见:信息学奥赛一本通 1436:数列分段II | 洛谷 P1182 数列分段 Section II
该题和上题本质上完全相同,解题代码自然也是一样的。

【题解代码】

解法1:二分答案
  • 写法1:
#include
using namespace std;
int a[100005], n, m;
bool check(int x)//满足每个子段的和<=x 
{
	int sum = 0, ct = 1;
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] > x)
			return false;
		if(sum+a[i] <= x)
			sum += a[i];
		else
		{
			ct++;
			sum = a[i];
		}
	}
	return ct <= m;
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	int l = 0, r = 1e9;
	while(l < r)
	{
		int mid = (l+r)/2;
		if(check(mid))
			r = mid;
		else
			l = mid+1;
	}
	cout << l;
	return 0;
}
  • 写法2:
#include
using namespace std;
int a[100005], n, m, tsum;
bool check(int x)//满足每个子段的和<=x 
{
	int sum = 0, ct = 1;
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] > x)
			return false;
		if(sum+a[i] <= x)
			sum += a[i];
		else
		{
			ct++;
			sum = a[i];
		}
	}
	return ct <= m;
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
	{
		cin >> a[i];
		tsum += a[i];//tsum:所有数字的加和 
	}
	int l = 0, r = tsum;
	while(l <= r)
	{
		int mid = (l+r)/2;
		if(check(mid))
			r = mid-1;
		else
			l = mid+1;
	}
	cout << l;
	return 0;
}

你可能感兴趣的:(OpenJudge题解,信息学奥赛一本通题解,洛谷题解,二分搜索,贪心算法,子段和,月度开销,编程题解)