版权声明:EL PSY CONGROO ! https://blog.csdn.net/THIS_IS_HPQ/article/details/81104059
题目背景
大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出0 0。
不过,已经修改数据,保证每个人都有活可干。
题目描述
现在要把m本有顺序的书分给k给人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。
现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
输入输出格式
输入格式:
第一行两个整数m,k;(k≤m≤500)
第二行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
这道题可以用DP解决,但是二分+贪心也是一种非常不错的解法。
我在这里谈一谈后者。
二分答案
通常,当我们看到题目要求XXXXX的最大值最小是多少,又或者是XXXXXX的最小值最大是多少,这道题十有八九要用二分了。用二分发能在O(nlogn)的复杂度里枚举可能的答案,再写一个判断函数去判断二分出的值的大小。
值得注意的是,运用二分法的时候,答案一定要满足单调性,不然无法二分。
书的复制这道题,我们可以二分出每个人抄写的时间。然后从最后一个人开始枚举(题目中要求前面的人尽量抄写的少),用贪心的思想记录下每个人能够复制的书。如果所有书能够抄完,说明二分出的时间>=最小时间,如果不能,说明二分出的时间<=最小时间,就再次进行二分。
二分最终得出的值就是正确答案了,从前往后输出即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[10001],k,m,sum=0,L[10001],R[10001];//ai表示编号为i的书的页数
bool check(int x)//二分判断答案正确性,这里我是在判断完正确答案之后再贪心求出答案。
{
int s,tot=k,book=m;//book表示当前的书的标号
s=x;//s表示当前的人剩余的时间
while(tot&&book)
{
if(s>=a[book])
{
s-=a[book];
book--;
}
else
{
s=x;
tot--;
}
}
if(book==0)
return 0;
else
return 1;
}
int main()
{
cin>>m>>k;
if(m==k&&m==0)
return 0;
for(int i=1;i<=m;i++)
{
cin>>a[i];
sum+=a[i];
}
int l=sum/k,r=sum;
while(l<r)//二分
{
int mid=(l+r)>>1;
if(check(mid))
l=mid+1;
else
r=mid;
}
int t=l,book=m;
for(int i=k;i>=1;i--)
{
t=l;
R[i]=book;
while(t-a[book]>=0&&book>=1)
{
t-=a[book];
book--;
}
L[i]=book+1;
}
for(int i=1;i<=k;i++)
cout<<L[i]<<' '<<R[i]<<endl;
return 0;
}