UVA - 714 Copying Books(二分+贪心)

在印刷术发明之前,书籍复制全靠手工重写。本文探讨了如何将书籍高效分配给多位抄写员,以减少完成所有书籍复制所需的时间,并提供了一种算法解决方案。


  Copying Books 

Before the invention of book-printing, it was very hard to make a copy of a book. All the contents had to be re-written by hand by so called scribers. The scriber had been given a book and after several months he finished its copy. One of the most famous scribers lived in the 15th century and his name was Xaverius Endricus Remius Ontius Xendrianus (Xerox). Anyway, the work was very annoying and boring. And the only way to speed it up was to hire more scribers.


Once upon a time, there was a theater ensemble that wanted to play famous Antique Tragedies. The scripts of these plays were divided into many books and actors needed more copies of them, of course. So they hired many scribers to make copies of these books. Imagine you have m books (numbered $1, 2, \dots, m$) that may have different number of pages ( $p_1, p_2, \dots, p_m$) and you want to make one copy of each of them. Your task is to divide these books among k scribes, $k \le m$. Each book can be assigned to a single scriber only, and every scriber must get a continuous sequence of books. That means, there exists an increasing succession of numbers $0 = b_0 <b_1 < b_2, \dots < b_{k-1} \le b_k = m$ such that i-th scriber gets a sequence of books with numbers between bi-1+1 and bi. The time needed to make a copy of all the books is determined by the scriber who was assigned the most work. Therefore, our goal is to minimize the maximum number of pages assigned to a single scriber. Your task is to find the optimal assignment.

Input 

The input consists of N cases. The first line of the input contains only positive integer N. Then follow the cases. Each case consists of exactly two lines. At the first line, there are two integers m and k$1 \le k \le m \le 500$. At the second line, there are integers $p_1, p_2, \dots p_m$ separated by spaces. All these values are positive and less than 10000000.

Output 

For each case, print exactly one line. The line must contain the input succession $p_1, p_2, \dots p_m$ divided into exactly k parts such that the maximum sum of a single part should be as small as possible. Use the slash character (`/') to separate the parts. There must be exactly one space character between any two successive numbers and between the number and the slash.


If there is more than one solution, print the one that minimizes the work assigned to the first scriber, then to the second scriber etc. But each scriber must be assigned at least one book.

Sample Input 

2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100

Sample Output 

100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100


题目大意:
一个抄写员要抄n本数,每本书有1<= x <= 10000000页,现在把这些数分配给k个抄书员,要求分配给某个抄写员的编号是连续的,而且每个抄写员的抄写速度是相同的,求所有书抄完所用的最少时间的分配方案。

解题思路:
最大值最小化问题,所花的总时间取决于所有抄写员中所花时间最多的那个人。
该问题可以等价于把n个正整数划分为m个连续子序列。使得其中最大的子序列的和最小。

先用二分法求出是否能满足最大的区间,然后由该区间从后向前遍历,先标记一遍分割的节点,再向前逐个遍历,直到所有的所有的分割线都用完为止结束标记。接着遇到标记为1的就打印分割线,否则不打印分割线。

注意:由于每个数的值不小于1000000,二分时y的值可能很大要用long long 保存。


#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll N = 5005;

ll max,min,num[N];
bool vis[N];
ll m,n;
bool separate(ll x) {
	ll k = 0;
	ll sum = 0;
	for(int i = 0; i < n; i++) {
		if(sum + num[i] <= x) {
			sum += num[i];
		}else {
			k++;
			sum = num[i];
		}
		if(k > m-1) {
			return false;
		}
	}
	return true;
}
ll bitSearch() {
	ll x = min, y = max, mid;
	while(x < y) {
		mid = x + (y - x)/2;
		if(separate(mid)) {
			y = mid;
		}else {
			x = mid + 1;
		}
	}
	return x;
}
int main() {
	int t;
	scanf("%d",&t);
	while(t--) {
		scanf("%lld%lld",&n,&m);
		min = -1;
		max = 0;
		for(int i = 0; i < n; i++) {
			scanf("%lld",&num[i]);
			if(min < num[i]) {
				min = num[i];
			}
			max += num[i];
		}
		memset(vis,0,sizeof(vis));
		ll res = bitSearch();
		ll sum = 0,k = 0;
		for(int i = n-1; i >= 0; i--) {
			if(sum + num[i] <= res) {
				sum += num[i];
			}else {
				sum = num[i];
				k++;
				vis[i] = true;
			}
		}
		for(int i = 0; i < n && k < m-1; i++) {
			if(!vis[i]) {
				vis[i] = true;
				k++;
			}
		}
		for(int i = 0; i < n-1; i++) {
			printf("%lld ",num[i]);
			if(vis[i]) {
				printf("/ ");
			}
		}
		printf("%lld\n",num[n-1]);
	}
	return 0;
}


你执行了: ```sql SHOW PROCESSLIST; ``` 输出如下(简化): ``` +----+------+---------------------+-----------+---------+---------------------+-------+-------+-----------------------------+ | Id | User | Host | Db | Command | ConnectionStartTime | Time | State | Info | +----+------+---------------------+-----------+---------+---------------------+-------+-------+-----------------------------+ | 0 | root | 192.168.91.1:55489 | | Sleep | 2025-11-09 17:09:02 | 22801 | EOF | SELECT DATABASE() | | 1 | root | 192.168.91.1:55499 | | Sleep | 2025-11-09 17:09:23 | 1832 | EOF | SELECT * FROM information_...| | 17 | root | 192.168.91.130:52620 | | Query | 2025-11-09 23:00:45 | 0 | OK | SHOW PROCESSLIST | | 2 | root | 192.168.91.1:55542 | school_db | Sleep | 2025-11-09 17:10:30 | 1842 | EOF | SELECT DATABASE() | +----+------+---------------------+-----------+---------+---------------------+-------+-------+-----------------------------+ ``` --- ### ✅ `EOF` 和 `OK` 的含义 | 状态 | 含义 | 说明 | |------|------|------| | **EOF** | End Of File | 表示查询已经完成,连接处于空闲状态(Sleep) | | **OK** | Operation Completed Successfully | 表示当前正在执行的语句已经成功完成(如 SHOW PROCESSLIST) | --- ### ✅ 各字段详解 | 字段名 | 含义 | |--------|------| | `Id` | 线程 ID,可以用于 `KILL [Id]` 来终止连接 | | `User` | 登录用户 | | `Host` | 客户端的 IP 和端口 | | `Db` | 当前连接使用的数据库,为空表示未使用 | | `Command` | 当前线程正在执行的命令类型(如 `Sleep`, `Query`, `Connect`) | | `ConnectionStartTime` | 连接建立时间 | | `Time` | 当前线程处于当前状态的时间(单位:秒) | | `State` | 状态,如 `EOF`, `OK`, `Sending data`, `Locked` 等 | | `Info` | 当前正在执行的 SQL 语句(如果未执行 SQL,则为空或显示元数据查询) | | `IsPending` | 是否为待处理连接(通常为 `false`) | --- ### ✅ 常见 `Command` 类型 | Command | 含义 | |---------|------| | `Sleep` | 空闲连接,未执行任何查询 | | `Query` | 正在执行查询 | | `Connect` | 正在连接中(认证阶段) | --- ### ✅ 常见 `State` 状态 | State | 含义 | |-------|------| | `EOF` | 查询结束,连接空闲 | | `OK` | 最近执行的命令已成功完成 | | `Sending data` | 正在向客户端发送数据 | | `Locked` | 等待表锁或行锁释放 | | `Waiting for query cache lock` | 等待查询缓存锁 | | `Copying to tmp table` | 正在将数据复制到临时表(可能性能问题) | --- ### ✅ 如何判断是否需要清理连接? - 如果看到大量 `Sleep` 状态且 `Time` 很大(如几千秒),说明这些是空闲连接。 - 可以通过以下命令终止空闲连接(例如 ID 为 0): ```sql KILL 0; ``` --- ### ✅ 示例:查看当前所有空闲连接 ```sql SELECT * FROM information_schema.processlist WHERE command = 'Sleep' AND time > 60; ``` --- ### ✅ 总结 | 状态 | 含义 | |------|------| | `EOF` | 查询已完成,连接空闲 | | `OK` | 最近执行的语句已成功完成 | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值