zoukankan      html  css  js  c++  java
  • UVa 714 Copying books 贪心+二分 最大值最小化

    题目大意:

    要抄N本书,编号为1,2,3...N, 每本书有1<=x<=10000000页, 把这些书分配给K个抄写员,要求分配给某个抄写员的那些书的编号必须是连续的。每个抄写员的速度是相同的,求所有书抄完所用的最少时间的分配方案。

     

    题目中的要求是去求划分的子序列的最大值尽量小,最大值最小化,如果从划分的角度看,无法获得好的思路,我们可以从值得角度考虑,所要求的最小的最大值必定是从[amax,sum(总和)]中取得的,那么我们可以二分法的方式猜测一个数字,看它是否满足要求,如果满足要求,我们可以继续缩小范围。

    实现的另一个关键是划分,题目说如果有多解的话,前面的要求尽量小,那么我们的划分必然是从右往左划分,我们可以先做判断,如果都要求划分的值尽量达到最大值,它的划分个数小于要求的划分数,它就是满足条件的,因为我们可以将某一个划分组的序列拆解下来(子序列在拆解),它也一定是满足条件的,因为小于最大值肯定是对的,在贪心的过程中,一旦还需要划分组数正好等还剩下的整数数量的话,直接将每一个数作为一个划分组即可。

    下面一篇博文将重点介绍一下二分查找的有关细节。

     1 #include<cstdio>
     2 #include<cstring>
     3 #define MAXN 505
     4 using namespace std;
     5 int num[MAXN];
     6 int mark[MAXN];
     7 int n,m,k;
     8 long long low=-1,high=0;
     9 void init(){
    10     low = -1;
    11     high = 0;
    12     memset(mark,0,sizeof(mark));
    13 }
    14 bool solve(long long mid){
    15     //进行判断是否可以划分为某个最小最大值的序列
    16     long long sum=0;
    17     int t=1;
    18     for(int i = m-1;i >=0 ; i--){
    19         if(sum + num[i] > mid){
    20             sum = num[i];
    21             t++;
    22             if(t > k)
    23                 return false;
    24         }
    25         else
    26             sum += num[i];
    27     }
    28     return true;
    29 }
    30 void print(long long s){
    31     long long sum = 0;
    32     int t = 1,i,j;
    33     for(i = m-1;i >=0 ; i--){
    34         if(sum + num[i] > s){
    35             //贪心的关键,尽量值靠近最大值
    36             sum = num[i];
    37             mark[i] = 1;
    38             t++;
    39         }
    40         else{
    41             sum +=num[i];
    42         }
    43         if(k - t == i + 1){
    44             //贪心的关键
    45             //如果剩下来的值的数量正好等于要划分的组数那么每一个数为一组
    46             for(j = 0 ;j <= i; j++){
    47                 mark[j] = 1;
    48             }
    49             break;
    50         }
    51     }
    52     for(i = 0 ;i < m-1 ; i++){
    53         printf("%d ",num[i]);
    54         if(mark[i]==1){
    55             printf("/ ");
    56         }
    57     }
    58     printf("%d
    ",num[m-1]);
    59 }
    60 int main(){
    61     long long left,right,mid;
    62     scanf("%d",&n);
    63     while(n--){
    64         init();
    65         scanf("%d%d",&m,&k);
    66         for(int i = 0 ; i < m ;i++){
    67             scanf("%d",&num[i]);
    68             if(low < num[i])
    69                 low = num[i];
    70             high+=num[i];
    71         }
    72         left = low;
    73         right = high;
    74         while(left <= right){
    75             memset(mark,0,sizeof(mark));
    76             mid = left + (right - left)/2;
    77             if(solve(mid)){
    78                 right = mid - 1;
    79             }
    80             else
    81             {
    82                 left = mid + 1;
    83             }
    84         }
    85         print(left);
    86     }
    87     return 0;
    88 }
  • 相关阅读:
    加载数据量大,页面卡死解决办法
    [存档]开启window7的隐藏功能虚拟wifi
    IIS发布Asp.Net网站注意事项
    [转载]总结几种C#窗体间通讯的处理方法
    调整和删除Win7休眠文件Hiberfil.sys的方法技巧,释放系统空间! ...
    [存档]Div+Css布局中经常使用的小技巧合集
    Android AndroidManifest.xml 结构详解
    Android权限详细说明
    Activity 生命周期详解
    程序员的文采
  • 原文地址:https://www.cnblogs.com/fancy-itlife/p/4717578.html
Copyright © 2011-2022 走看看