例题8-10 抄书 UVa714

1.题目描述:点击打开链接

2.解题思路:本题要求“最大值最小”,这是一种很常见的优化目标。不妨考虑这个问题:能否把输入的序列划分为k个连续子列,使得所有的S(i)均不超过k?若定义一个谓词P(x),则让P(x)为真时x最小就是答案。那么如何求P(x)呢?根据题意可以选择如下贪心策略:从后往前扫描,每次都尽可能多的向左分,如果分不下去了,标记此时的位置。这样在O(N)时间内就能计算出P(x)的结果。然后就是“猜数字”游戏了:设序列的所有数之和为sum,在0~sum之间找一个P(x)为真的最小x。用二分查找即可。这样以来,整个过程的时间复杂度为O(N*logSUM)。注意:sum要用long long型!

3.代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;

const int maxn = 500 + 20;
int a[maxn], vis[maxn], k, n;
bool P(long long x)
{
	memset(vis, 0, sizeof(vis));
	int cnt = 0;//标记已经分出来的区间个数
	long long sum = 0;
	int i, ok = 0;
	for (i = n - 1; i >= 0; i--)
	{
		if (sum + a[i] <= x&&i >= k - cnt - 1)//贪心策略:在一定的范围内,尽量往左划分
			sum += a[i];
		else if (!sum&&a[i]>x)return false;
		else { ok = 1; vis[i] = 1; }
		if (ok){ cnt++; ok = 0; sum = 0; i++; }
	}
	if (cnt != k - 1)return false;
	return true;
}
int main()
{
	//freopen("test.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	while (T--)
	{
		memset(a, 0, sizeof(a));
		scanf("%d%d", &n, &k);
		long long sum = 0;//注意:由于所有数的和会超过int的范围,应该用long long
		for (int i = 0; i < n; i++)
		{
			scanf("%d", a + i);
			sum += a[i];
		}
		long long l = 0, r = sum;
		while (l < r)//二分查找
		{
			int m = l + (r - l) / 2;
			if (P(m))r = m;
			else l = m + 1;
		}
		P(l);//为了避免误差,再进行一次
		for (int i = 0; i < n; i++)
		if (!vis[i])printf("%d%c", a[i], i == n - 1 ? '\n' : ' ');
		else printf("%d / ", a[i]);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值