1. 程式人生 > >[貪心][前綴和] JZOJ P1795 教主的別墅

[貪心][前綴和] JZOJ P1795 教主的別墅

col sca get 最小 span 字符 con gpo abs

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",&n,&m);
37     gets(s);
38     greedy();
39     for (int i=0;i<m;i++) printf("%d ",fen[i]);  printf("\n");
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 } 

[貪心][前綴和] JZOJ P1795 教主的別墅