zoukankan      html  css  js  c++  java
  • 06:月度开销

    题目链接:http://noi.openjudge.cn/ch0111/06/

    总时间限制: 1000ms 内存限制: 65536kB
    描述
      农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。

      约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。

      约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。

    输入
      第一行包含两个整数N,M,用单个空格隔开。
      接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。
    输出
      一个整数,即最大月度开销的最小值。

    样例输入
    7 5
    100
    400
    300
    100
    500
    101
    400
    样例输出
    500
    输入输出样例说明
      若约翰将前两天作为一个月,第三、四两天作为一个月,最后三天各自作为一个月,则最大月度开销为500。其他任何分配方案都会比这个值更大。

    先看AC代码:(其实下面的代码二应该比较好理解。)

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 int check(long *a,long N,long long mid,long M);//
     5 int main()
     6 {
     7     long N,M;
     8     long *a=NULL,i;
     9     long long left=0,right=0,mid=0;
    10     int res;
    11     
    12     scanf("%ld%ld",&N,&M);
    13     a=(long*)malloc(N*sizeof(long));
    14     memset(a,0,N);
    15     for(i=0;i<N;i++)
    16     {
    17         scanf("%ld",&a[i]);
    18         if(a[i]>left) left=a[i];
    19         right=right+a[i];
    20     }
    21 
    22     while(left<right)
    23     {
    24         mid=left+(right-left)/2;
    25         res=check(a,N,mid,M);
    26         if(res==1) right=mid;
    27         else left=mid+1;
    28     }
    29     printf("%lld
    ",left);
    30     return 0;
    31 }
    32 
    33 //假设最大月开销为mid,统计需要分成多少个月.然后看月的个数是否太多或太少
    34 int check(long *a,long N,long long mid,long M)
    35 {
    36     long count=1,i,temp=0;
    37     for(i=0;i<N;i++)
    38     {
    39         if(temp+a[i]<=mid) temp=temp+a[i];//把第i天归入到当前第count月
    40         else if(a[i]<=mid)//可以独立成一个月
    41         {
    42             count++;//开始一个新的月
    43             temp=a[i];
    44             if(count>M) return -1;//最大月开销太小,导致分的组太多了。
    45         }
    46         else return -1;//最大月开销mid太小了,导致某些开销比较大的天单独构成一个月都不行。
    47     }
    48     if(count>M) return -1;
    49     else if(count<=M) return 1;//最大月开销mid太大了,导致分的组太少了
    50 }

    思路说明:

    题目的意思一定要理解清楚!!!“合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。”   “输出一个整数,即最大月度开销的最小值。

    就是把所有天划分为若干个段,先求出每个段里面的数字之和,然后统计各段累加和的最大值,这个值要尽可能小。现在要找的就是这个“累加和的最大值”   最小可以是多少。

     首先,这个题目应该二分,因为解的区间是可以明确的,可以对该区间进行二分求的真正的解。

    假设二分的区间left~right,其中left是n天开销中最大的那一个数字,right是n天开销的总和。  (设想一个极限情况,要使得每一个月开销尽量小,那么每一天都单独做一个月就好啦,于是这个时候的月开销最大值就是n天中每天开销最大的值,所以left可以取max(a1,......,an)。    再设想另一种极限情况,把所有天合并在一起组成一个月,那么这个时候月开销最大值就是sum(a1,a2,......,an),所以right取值就是n天的累加和。)

    需要注意的一个地方是二分循环部分的代码:

    1     while(left<right)
    2     {
    3         mid=left+(right-left)/2;
    4         res=check(a,N,mid,M);
    5         if(res==1) right=mid;
    6         else left=mid+1;
    7     }
    8     printf("%lld
    ",left);

    其中left=mid+1这里必须加上1,否则可能会死循环的。

    另外,输出值是left。这个地方也要特别注意。(请自己脑补为何是left吧)

    关于子函数check(),嗯代码注释讲的很清晰,不说了。

    代码二:(其实我觉得这段代码更好理解。人就是这样,越学习越进步哈哈)

    区别就只在第21到28行

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 int check(long *a,long N,long long mid,long M);//
     5 int main()
     6 {
     7     long N,M;
     8     long *a=NULL,i;
     9     long long left=0,right=0,mid=0;
    10     int res;
    11     
    12     scanf("%ld%ld",&N,&M);
    13     a=(long*)malloc(N*sizeof(long));
    14     memset(a,0,N);
    15     for(i=0;i<N;i++)
    16     {
    17         scanf("%ld",&a[i]);
    18         if(a[i]>left) left=a[i];
    19         right=right+a[i];
    20     }
    21     int ans=left;
    22     while(left<=right)
    23     {
    24         mid=left+(right-left)/2;
    25         res=check(a,N,mid,M);
    26         if(res==1) { ans=mid; right=mid-1; }
    27         else left=mid+1;
    28     }
    29     printf("%d
    ",ans);
    30     return 0;
    31 }
    32 
    33 //假设最大月开销为mid,统计需要分成多少个月.然后看月的个数是否太多或太少
    34 int check(long *a,long N,long long mid,long M)
    35 {
    36     long count=1,i,temp=0;
    37     for(i=0;i<N;i++)
    38     {
    39         if(temp+a[i]<=mid) temp=temp+a[i];//把第i天归入到当前第count月
    40         else if(a[i]<=mid)//可以独立成一个月
    41         {
    42             count++;//开始一个新的月
    43             temp=a[i];
    44             if(count>M) return -1;//最大月开销太小,导致分的组太多了。
    45         }
    46         else return -1;//最大月开销mid太小了,导致某些开销比较大的天单独构成一个月都不行。
    47     }
    48     if(count>M) return -1;
    49     else if(count<=M) return 1;//最大月开销mid太大了,导致分的组太少了
    50 }
  • 相关阅读:
    android ndk通过遍历和删除文件
    SVN提交忽略*.class、.classpath、.mymetadata、.project、.settings、.myeclipse和其他非版本控制文件
    JDBC加载过程
    JVMTI 中间JNI系列功能,线程安全和故障排除技巧
    【Python】Python与文本处理langid工具包的文本语言检测和歧视
    SQL 存储过程 分页
    Android:仿手机QQ朋友动态ListView
    再说Java EE
    辛星与您解读PHP页面跳转的几种实现方式
    VS2010 使用TeeChart画图控件
  • 原文地址:https://www.cnblogs.com/huashanqingzhu/p/5607503.html
Copyright © 2011-2022 走看看