洛谷 P1281 书的复制(二分答案 输出方案)

书的复制

题目背景

大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出 0 0

不过,已经修改数据,保证每个人都有活可干。

题目描述

现在要把 m m m 本有顺序的书分给 k k k 个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。

现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

输入格式

第一行两个整数 m , k m,k m,k

第二行 m m m 个整数,第 i i i 个整数表示第 i i i 本书的页数。

输出格式

k k k 行,每行两个整数,第 i i i 行表示第 i i i 个人抄写的书的起始编号和终止编号。 k k k 行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。

样例 #1

样例输入 #1

9 3
1 2 3 4 5 6 7 8 9

样例输出 #1

1 5
6 7
8 9

提示

1 ≤ k ≤ m ≤ 500 1\le k \le m \le 500 1km500

题意:

P1182 数列分段 Section II的输出方案版本

思路:

先二分答案,得出所有人中分得页数最大值的最小值 l,之后利用 l 进行一遍倒序枚举(二分过程中的枚举我们也设置为倒序,这样方便后面的输出方案),将方案存进 x、y 数组,结尾倒序输出方案。

设置两个数组 x[505],y[505] 表示:各人负责书的起始编号和结束编号。

注意,假设前面有部分人不必使用时存储的就是00 00,所以需要初始化为00。

时间复杂度:(sum 表示所有元素之和)

O ( n l o g s u m ) O(nlogsum) O(nlogsum)

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
//#define map unordered_map

//int128 ORZ

/*
__int128 read() {
	__int128 x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
		if (ch == '-')f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

void print(__int128 x) {
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
*/
int m, k;
const int N = 510;
int a[N], x[N], y[N];

bool check(int ans)
{
	int cnt = 1, tmp = 0;
	for (int i = m; i >= 1; --i)
	{
		if (tmp + a[i] <= ans) {
			tmp += a[i];
		}
		else {
			if (a[i] <= ans) tmp = a[i], ++cnt;
		}
	}
	return cnt <= k;
}

inline void solve()
{
	cin >> m >> k;
	int r = 0;
	for (int i = 1; i <= m; ++i)
	{
		cin >> a[i], r += a[i];
	}
	int l = *max_element(a + 1, a + m + 1);

	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}

	//初始化为0,由于是全局数组,因此省略

	y[1] = m;	//第一人结束点为 m
	int tmp = 0, cnt = 1;
	for (int i = m; i >= 1; --i) {	//倒序遍历
		if (tmp + a[i] <= l) {
			tmp += a[i];
		}
		else {	//当前书无法给第 cnt 个人抄写
			if (a[i] <= l) {
				//  cnt 人开始编号定为 i + 1
				//  cnt + 1 人结束编号为 i
				tmp = a[i], x[cnt] = i + 1, y[++cnt] = i;	
			}
		}
	}
	x[cnt] = 1;	//最后一人起始点为1

	//最后应该倒序输出
	for (int i = k; i >= 1; --i)
	{
		cout << x[i] << " " << y[i] << '\n';
	}
}

signed main()
{
	//	ios::sync_with_stdio(false);
	//	cin.tie(nullptr), cout.tie(nullptr);

	int _ = 1; //cin >> _;

	while (_--)
	{
		solve();
	}

	return 0;
}

你可能感兴趣的:(二分,算法,c++)