zoukankan      html  css  js  c++  java
  • [贪心][前缀和] JZOJ P1795 教主的别墅

    Description

    【题目背景】
      LHX教主身为宇宙第一富翁,拥有一栋富丽堂皇的别墅,由于别墅实在太大了,于是教主雇佣了许许多多的人来负责别墅的卫生工作,我们不妨称这些人为LHXee。

    【题目描述】
      教主一共雇佣了N个LHXee,这些LHXee有男有女。
      教主的大别墅一共有M个房间,现在所有的LHXee在教主面前排成了一排。教主要把N个LHXee分成恰好M个部分,每个部分在队列中都是连续的一段,然后分别去打扫M个房间。
      教主身为全世界知识最渊博的人,他当然知道男女搭配干活不累的道理,以及狼多羊少,羊多狼少的危害之大。所以教主希望一个分配方式,使得所有小组男女个数差的最大值最小。
      教主还希望你输出从左到右,每个组的人数。
      如果有多种人数组合都能达到最优值,教主希望你分别告诉他这些方案中字典序最小和最大的方案。换句话说,你需要找到两种方案,这两种方案满足所有组男女个数差最大值最小的前提下,第一种方案(字典序最小)要越靠前的组人数越少,也就是让第一个小组人尽量少,并且在第一个小组人尽量少的前提下,让第二个小组的人尽量少,依此类推;第二种方案(字典序最大)则要让越靠前的组人数越多。
     

    Input

      输入的第1行为两个正整数N与M,用空格分隔。
      第2行包含一个长度为N的串,仅由字符组成,第i 个字符为0表示在这个位置上的LHXee为女生,若为1则为男生。

    Output

      输出文件包含两行,每行M个正整数,正整数之间用空格隔开,行末无多余空格。这M个正整数从左到右描述了你所分的每个组的人数。
      第1行为字典序最小的方案,第2行为字典序最大的方案。
     

    Sample Input

    8 3
    11001100
    

    Sample Output

    1 2 5
    5 2 1

    Hint

    【样例说明】
      字典序最小的方案按1, 10, 01100分组,每组男女个数差的最大值为1,为最小。
      字典序最大的方案按11001, 10, 0分组。

    【数据规模】
      对于40%的数据,有N ≤ 100;
      对于50%的数据,有N ≤ 1000;
      对于65%的数据,有N ≤ 100000;
      对于100%的数据,有N ≤ 5000000,M ≤ N且M ≤ 100000。

    【提示】
    关于字典序:
    比较S1[N]与S2[N]的字典序大小,可以找到S1[N]与S2[N]中第1个不相同数字S1[i]与S2[i](即有对于所有1≤k<i,都有S1[k] =S2[k],但S1[i]≠S2[i])。如果S1[i]<S2[i],那么说S1[N]字典序比S2[N]小,否则说S1[N]字典序比S2[N]大。

    题解

    • 我们把0当成-1做,跑一边前缀和
    • 用zero记录下有多少个位置为0
    • 如果zero比分组的组数多而且前缀和最后不为0,将x定为0(后面会解释x是什么)
    • 否则,将x定为abs(abs(sum[n-1])-1)/m+1(也就是将sum平均分到每一组的差值,向上取整)
    • 然后我们就可以模拟了,如果当前分了cnt>=m-1,将后面所有的数塞到第m组
    • 如果abs(sum[n-1]-sum[i])/(m-cnt-1)<=x,也就是将i处断后,后面的平均差值小于x,那当然是可以断的
    • 那么怎么满足字典序最小的条件呢?
    • 当然是有的取尽量先取
    • 就字典序最大时,将数组反过来再跑一遍,从大到小输出

    代码

     1 #include<cstdio>  
     2 #include<cstring>
     3 #include<iostream> 
     4 #include<algorithm>
     5 using namespace std;
     6 int n,m,sum[5000010],ans,fen[5000010];
     7 char s[5000010]; 
     8 int abs(int x){return x>0?x:-x;}  
     9 void greedy()
    10 {
    11     int cnt=0,num=-1,zero=0,k=0;
    12     memset(sum,0,sizeof(sum));
    13     for (int i=0;i<=n-1;i++)
    14     {
    15         if (s[i]=='1') k++; else k--;
    16         sum[i]=k;
    17         if (!k) zero++;
    18     }
    19     if (!sum[n-1]&&zero>=m) ans=0; else ans=abs(abs(sum[n-1])-1)/m+1;
    20     for (int i=0;i<=n-1;i++)
    21     {
    22         if (cnt>=m-1)
    23         {
    24             fen[cnt++]=n-i;
    25             break;
    26         }
    27         if (abs(sum[n-1]-sum[i])<=ans*(m-cnt-1))
    28         {
    29             fen[cnt++]=i-num;
    30             num=i;
    31         }
    32     }
    33 }
    34 int main()
    35 {  
    36     scanf("%d%d
    ",&n,&m);
    37     gets(s);
    38     greedy();
    39     for (int i=0;i<m;i++) printf("%d ",fen[i]);  printf("
    ");
    40     reverse(&s[0],&s[n]);
    41     greedy();
    42     for (int i=0;i<m;i++) printf("%d ",fen[m-i-1]);  
    43     return 0;
    44 } 
  • 相关阅读:
    PAT 甲级 1132 Cut Integer (20 分)
    AcWing 7.混合背包问题
    AcWing 9. 分组背包问题
    AcWing 5. 多重背包问题 II
    AcWing 3. 完全背包问题
    AcWing 4. 多重背包问题
    AcWing 2. 01背包问题
    AcWing 875. 快速幂
    AcWing 874. 筛法求欧拉函数
    AcWing 873. 欧拉函数
  • 原文地址:https://www.cnblogs.com/Comfortable/p/8428199.html
Copyright © 2011-2022 走看看