【全网首发】洛谷P1281书的复制

发布时间:2023年12月26日

题目

题目背景

大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出 0 0。不过,已经修改数据,保证每个人都有活可干。

题目描述

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

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

输入格式

第一行两个整数 m,k。

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

输出格式

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

样例输入 #1
9 3
1 2 3 4 5 6 7 8 9

样例输出 #1
1 5
6 7
8 9

说明/提示

1<=k<=m<=500

解题思路

把m个数分成k组,使每组数的和尽量平均。

那么,我们需要二分的其实是这个“平均”的值,也就是复制时间

把二分出来的复制时间代到输入数据中,就可以判断这个复制时间是否可行。根据题目要求:

尽可能让前面的人少抄写

emmm这个怎么实现呢?

其实啊,我们这么想:我们面前有一面长长的桌子,桌子上从左到右整整齐齐摆着m本书,有k个可怜的娃要去抄书。要使前面的人少抄写,也就是抄靠右边的书的娃要多抄。那么,就要从抄靠右边的书的娃开始枚举,让他尽量多抄。


伪代码:

for m~1//给娃加书
{
	if 当前这个娃抄的书超过选择的复制时间 
    	then 把这本书留下,换下一个娃来抄
        if 没有娃能来抄书了,就说明复制时间太短了
}
	
最后要特判:最后抄书最少的娃能不能抄完书,不能的话也是失败的!

于是,通过以上操作变可以二分出我们心心念念的复制时间了!!

然后,又是一个代入的过程,我们把得出的复制时间代入输入数据,再次从右到左枚举一次,就可以得出每个人抄书的范围。

但是,答案要从左往右输出,所以拿个数组记录下来就好了!!!

那么就要考虑一下求什么:“使得复制时间最短”;

那么f[i][j]就表示前i个人抄j本书的最短时间

有三种可能是最小时间:

1.原先所求

2.前i-1个人抄k本书

3.抄k到j本书的总页数所花时间(若将时间与页数的数值看成相同的)

边界:

一个人抄任意第i本书的时间都是第i本书的前缀和

其次,这是一个递归

求:“设计一种方案”

边界:没人直接return ;

二分答案。先使用二分答案求出复制时间(抄写页数最多的人用去的时间)最短是多少。然后使用一定的小技巧使得前面的人尽量少抄写。

二分答案的部分应该都会吧,简略的讲一下: 二分复制时间,判断在当前复制时间的限制下抄完?m?本书需要几个人,如果需要的人数小于等于?k?的话就可以见小复制时间,如果大于?k?的话就要增加复制时间。

小技巧: 我们二分答案求出最少的复制时间,然后从第k~1?个人、第 m~1?本书开始给他们布置任务,如果当前人不能再抄更多的书了(再抄更多的书就要超出我们二分出的最短复制时间了)就换下一个人,这样使得后面的人抄尽可能多的书,前面的人就可以抄尽可能少的书。

想学习二分基础,可以看看这篇文章——C++:第十讲二分查找-CSDN博客

AC :

#include<bits/stdc++.h>
const int N=505;
using namespace std;
int m,k,p[N],f[N][N],sum[N],maxmin;
void Print(int x)
{
	if(!x)	return;
	for(int i=x;i>=0;i--)
	{
		if(sum[x]-sum[i-1]>maxmin||i==0)
		{
			Print(i);
			cout<<i+1<<" "<<x<<endl;
			break;
		}
	}
}
int main()
{
	cin>>m>>k;	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=m;i++)	
	{
		cin>>p[i];
		sum[i]=sum[i-1]+p[i];
		f[i][1]=sum[i];
	}
	f[1][0]=0;
	for(int i=2;i<=k;i++)
		for(int j=1;j<=m;j++)
			for(int q=1;q<j;q++)
				f[j][i]=min(f[j][i],max(f[q][i-1],sum[j]-sum[q]));
	maxmin=f[m][k];
	Print(m);
	return 0;
}

结尾

呼——,写了那么长的代码,希望各位多多关注,点个不要钱的赞,一定会给大家带来更多精彩内容。

如果你喜欢或想了解一下其他的算法,可以看看以下这些:

前缀和与差分:

C++:第九讲前缀和与差分-CSDN博客

贪心:

C++:第八讲贪心算法1-CSDN博客

排序:

C++:第七讲冒泡排序-CSDN博客

函数:

C++第6讲max和min函数_c++ min函数-CSDN博客

C++第五讲函数初步-CSDN博客

for循环&数组:

C++第四讲for循环及数组-CSDN博客

if语句&else语句及运算:

C++第三讲:C++中的逻辑运算符及if else语句-CSDN博客

基础:

C++第二讲输入与输出-CSDN博客

C++第一讲认识C++编译器-CSDN博客

最后认识一下,我是爱编程的喷火龙廖,我们有缘再见!

文章来源:https://blog.csdn.net/2301_81328824/article/details/135208137
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。