题目背景
大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出 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博客
#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博客
最后认识一下,我是爱编程的喷火龙廖,我们有缘再见!