集训第四周(高效算法设计)B题 (二分查找优化题)

---恢复内容开始---

Description

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 calledscribers. 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 ) that may have different number of pages ( ) and you want to make one copy of each of them. Your task is to divide these books among k scribes, . 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  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. At the second line, there are integers  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  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

这道题首先应该想好思路,这道题其实是一个找限值的过程,比如说一个排列,我划分很多的区间,要求每一个区间都不能大于某个值,设这个值为m,那么这个值最小应该是多少,以例1为例,最小应为900,以例2为例,最小应该为100,为什么?因为你有可能将排列中某一个值划为一个区间,如果你限值小于900,那么900就进不了任何区间,划分就会出问题了

同样我们还可以得出m最大值应该是排列相加的总和(把整个排列划为一个区间)。

既然找好了限值的范围,那么限值有什么用呢?可以使用限值作为划多少区间的依据,我可以从从限值的最小处枚举到最大处,直到限值正好可以使区间划分为k个(注意这里可能有个精度问题)。可是枚举的方法虽然可以找到需求的那个值,但是。。。

超时啊,

虽然数组的长度只有500,但是数组元素的最大值可达一千万,一千万乘以五百是什么概念?这样枚举时间复杂度为(n^2)*m,超时。。

最好的方法是二分查找进行优化,以限值去判断,如果说这个限值使得区间划分数大于k,那么限值肯定要再大一些,往右查,反之则往左查

这样的时间复杂度是m*n*logn,时间性能大大提高...

#include"iostream"
#include"algorithm"
#include"cstring"
#include"cstdio"
using namespace std;
int n,k;
int a[510];
long long tot;
int maxa;

void Init()
{
cin>>n>>k;
tot=0;
maxa=-1;
for(int i=0;i<n;i++)
{
    cin>>a[i];
    tot+=a[i];
    maxa=max(a[i],maxa);
}
//sort(a,a+n);
}

int need(long long num)
{
    long long c=0;
    int sum=1;
    for(int i=0;i<n;i++) if(c+a[i]<=num) c+=a[i];
    else {c=a[i];sum++;}
    return sum;
}

void print(long long num)
{
    int last[510];
   long long done = 0;
  memset(last, 0, sizeof(last));
  int remain = k;
  for(int i = n-1; i >= 0; i--) {
    if(done + a[i] > num || i+1 < remain) {
      last[i] = 1; remain--; done = a[i];
    }
    else {
      done += a[i];
    }
  }
  for(int i = 0; i < n-1; i++) {
    printf("%d ", a[i]);
    if(last[i]) printf("/ ");
  }
  printf("%d\n", a[n-1]);
}

void guess()
{
  long long l,r,m;
  l=maxa;r=tot;
  while(l<r)
  {
      m=l+(r-l)/2;
     if(need(m)<=k) r=m;
     else l=m+1;
  }
  print(l);
}

int main()
{
    int T,t;
    cin>>T;
    t=T;
    while(T--)
    {
        Init();
        guess();
    }
    return 0;
}
时间: 2024-10-27 12:35:47

集训第四周(高效算法设计)B题 (二分查找优化题)的相关文章

集训第四周(高效算法设计)N题 (二分查找优化题)

原题:poj3061 题意:给你一个数s,再给出一个数组,要求你从中选出m个连续的数,m越小越好,且这m个数之和不小于s 这是一个二分查找优化题,那么区间是什么呢?当然是从1到数组长度了.比如数组长度为10,你先找5,去枚举每一个区间为5的连续的数,发现存在这样的数,那么就可以继续往左找,反之则往右找,直到左右区间重合,即为正确答案,(有可能是右区间小于左区间,所以每次都应该求区间中点值) #include"iostream" #include"set" #incl

十大基础实用算法之归并排序和二分查找

归并排序 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 算法步骤: 1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置 3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4. 重复步骤3直到某一指针达到序列尾 5. 将另一序列剩下的所有元素直接复制到合并序列尾 用分治策略解决问题分为三步:分解

第八章 高效算法设计

分治法求最大连续和 注意范围是[x,y) #include<bits/stdc++.h> using namespace std; int maxsum(int *A,int x,int y){ if(y-x==1) return A[x]; int m=x+(y-x)/2; int maxs = max(maxsum(A,x,m),maxsum(A,m,y)); int v,L,R; v=0; L=A[m-1]; for(int i=m-1;i>=x;i--) L=max(L,v+=A

算法实现回顾1——二分查找

前话: 为什么写这个系列?算法的精髓除了在于算法的一个设计,更在于算法的一个好的设计.前者可能需求一个好的算法工程师,而后者则需求一个优秀的程序员.很多时候我们往往只希望去了解一种设计思路,但是对于程序员,一种优良的实现是非常重要的.实现的细节才决定成败.毕竟程序员面对和输出的都是程序,而不是思路. 引用: 你真的会二分查找吗?http://blog.csdn.net/int64ago/article/details/7425727二分查找,你真的会吗?http://www.ahathinkin

算法导论 2.3-5 二分查找

1.二分查找(Binary Search) 二分查找又称折半查找,它是一种效率较高的查找方法.    二分查找要求:线性表是有序表,即表中结点按关键字有序,并且表的存储结构为顺序结构.不妨设有序表是递增有序的. 2.二分查找的基本思想 二分查找算法思想: (1)首先确定该区间的中点位置: mid = ( left + right ) / 2; (2)然后将待查的K值与R[mid].key比较,若相等,则查找成功并返回此位置:否则须确定新的查找区间,继续二分查找,具体方法如下: ①若R[mid].

算法复习笔记:二分查找

在计算机科学中,折半搜索(英语:half-interval search),也称二分查找算法(binary search).二分搜索法.二分搜索.二分探索,是一种在有序数组中查找某一特定元素的搜索算法.搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束:如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较.如果在某一步骤数组为空,则代表找不到.这种搜索算法每一次比较都使搜索范围缩小一半. 实现: int bin

编程算法 - 快速排序(QuickSort)和二分查找(BinarySearch)

快速排序(QuickSort)和二分查找(BinarySearch) 本文地址: http://blog.csdn.net/caroline_wendy 快速排序和二分查找的定义, 网上书上都有, 本文主要是讲解如何写出这两个经典算法. 程序员必须掌握的两种算法, 使用任何语言, 使用纸都是必须的. 快速排序(C): /* * main.cpp * * Created on: 2014年9月10日 * Author: Spike */ #include <stdio.h> #include &

js基本算法:冒泡排序,二分查找

知识扩充: 时间复杂度:算法的时间复杂度是一个函数,描述了算法的运行时间.时间复杂度越低,效率越高. 自我理解:一个算法,运行了几次时间复杂度就为多少,如运行了n次,则时间复杂度为O(n). 1.冒泡排序 解析:1.比较相邻的两个元素,如果前一个比后一个大,则交换位置. 2.第一轮的时候最后一个元素应该是最大的一个. 3.按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较. 1 function sort(elements){ 2 for(v

在路上---学习篇(一)Python 数据结构和算法 (5)二分查找、二叉树遍历

独白: 利用算法进行查找指定元素,最近学习二分查找和二叉树遍历.二分查找前提是在有序中进行查找,二叉树引入了树的概念.树的概念其中有许多小知识点,也是一种新的数据结构.还是之前的感悟,需了解其本质才会写出更好的算法. 二分查找 二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用