教主的别墅 //2018.2.7

题目

Descriptionios

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

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

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

Output
  输出文件包含两行,每行M个正整数,正整数之间用空格隔开,行末无多余空格。这M个正整数从左到右描述了你所分的每一个组的人数。
  第1行为字典序最小的方案,第2行为字典序最大的方案。
【数据规模】
  对于40%的数据,有N ≤ 100;
  对于50%的数据,有N ≤ 1000;
  对于65%的数据,有N ≤ 100000;
  对于100%的数据,有N ≤ 5000000,M ≤ N且M ≤ 100000。ui


解题思路

贪心:

先求出男女生的差值尽可能平均分配到m个房间.【ans】

用O(n)的时间扫一遍即得答案.【注意还要倒着算一次最小字典序】

注意:差值为0(即男女生人数同样的时候)要特殊处理;


代码

#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
int n,m,ans,k,a[5000001],b[5000001]; char c; 

int abs(int x)
{  if (x>=0) return x; else return -x; }

int main()
{
    scanf("%d%d\n",&n,&m); 
    for (int i=1;i<=n;i++) 
    {
        cin>>c;
        if (c=='1') a[i]=a[i-1]+1; else a[i]=a[i-1]-1;  //前缀和
    }
    ans=abs(a[n])/m; //ans
    if (a[n]%m!=0) ans++; //向下取整
    if (a[n]==0) //处理特殊状况
    {
        k=0; 
        for (int i=1;i<=n;i++)
         if (a[i]==0) k++; 
         if (k>=m) ans=0; else ans=1;
    }
    int next=0,t=m; //next记录上一次分割的位置
    for (int i=1;i<=n;i++)
    {
      k=abs(a[n]-a[i])/(t-1);  //分割i-next这一部分,后面剩余的男女生的差值尽可能平均分值
      if (((abs(a[n]-a[i]))%(t-1))!=0) k++; //等同ans的处理
      if (k<=ans&&(abs(a[i]-a[next])<=ans))//若是男女生的差值尽可能平均分都容许的话就输出
      {
        printf("%d ",i-next); 
        next=i;
        t--; 
        if (t==1) //只剩下一份的状况
        {
          printf("%d ",n-next); //直接输出
          break;
        }
      } 
    }
    printf("\n"); 
    next=n; t=m; int o=0;  
    for (int i=n-1;i>=1;i--)//倒着再处理一次
    {
      k=abs(a[i])/(t-1); 
      if ((abs(a[i]))%(t-1)!=0) k++; 
      if ((abs(a[next]-a[i])<=ans)&&k<=ans)
      {
        b[++o]=next-i; //要用b数组储存起来,由于要再倒着输出
        next=i;
        t--;
        if (t==1)
        {
          b[++o]=next; 
          break; 
         }
      }
    }
    for (int i=o;i>=1;i--)
     printf("%d ",b[i]);
}