【BZOJ4069】【APIO2015】巴厘岛的雕塑 [贪心][DP]

巴厘岛的雕塑

Time Limit: 10 Sec  Memory Limit: 64 MB
[Submit][Status][Discuss]

Description

  印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。
  在这条主干道上一共有 N 座雕塑,为方便起见,我们把这些雕塑从 1 到 N 连续地进行标号,其中第 i 座雕塑的年龄是 Yi 年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛。
  下面是将雕塑分组的规则:
  这些雕塑必须被分为恰好 X 组,其中 A< = X< = B,每组必须含有至少一个雕塑,每个雕塑也必须属于且只属于一个组。同一组中的所有雕塑必须位于这条路的连续一段上。
  当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和。
  计算所有年龄和按位取或的结果。我们这个值把称为这一分组的最终优美度。
  请问政府能得到的最小的最终优美度是多少?
  备注:将两个非负数 P 和 Q 按位取或是这样进行计算的:
  首先把 P 和 Q 转换成二进制。
  设 nP 是 P 的二进制位数,nQ 是 Q 的二进制位数,M 为 nP 和 nQ 中的最大值。P 的二进制表示为 pM−1pM−2…p1p0,Q 的二进制表示为 qM−1qM−2…q1q0,其中 pi 和 qi 分别是 P 和 Q 二进制表示下的第 i 位,第 M−1 位是数的最高位,第 0 位是数的最低位。
  P 与 Q 按位取或后的结果是: (pM−1  OR  qM−1)(pM−2 OR qM−2)…(p1 OR q1)(p0 OR q0)。其中:
  0 OR 0=0
  0 OR 1=1
  1 OR 0=1
  1 OR 1=1

Input

  输入的第一行包含三个用空格分开的整数 N,A,B。

  第二行包含 N 个用空格分开的整数 Y1,Y2,…,YN。

Output

  输出一行一个数,表示最小的最终优美度。

Sample Input

  6 1 3
  8 1 2 1 5 4

Sample Output

  11

  explanation
  将这些雕塑分为 2 组,(8,1,2) 和 (1,5,4),它们的和是 (11) 和 (10),最终优美度是 (11 OR 10)=11。(不难验证,这也是最终优美度的最小值。)

HINT

  子任务 1 (9 分)
  1< = N< = 20
  1< = A< = B< = N
  0< = Yi< = 1000000000

  子任务 2 (16 分)
  1< = N< = 50
  1< = A< = B< = min{20,N}
  0< = Yi< = 10
 
  子任务 3 (21 分)
  1< = N< = 100
  A=1
  1< = B< = N
  0< = Yi< = 20

  子任务 4 (25 分)
  1< = N< = 100
  1< = A< = B< = N
  0< = Yi< = 1000000000

  子任务 5 (29 分)
  1< = N< = 2000
  A=1
  1< = B< = N
  0< = Yi< = 1000000000

Main idea

  将一个序列分为若干组,使得每组的和OR起来的值最小。

Solution

  根据题意,要使最终的答案最小,可以想到利用贪心,从高到低枚举答案的每一位,如果能取0则取0,否则取1。

  问题转化为如何判断答案的某一位能否取0,我们考虑用DP解决这个问题。假设当前枚举到第pos位。

  令f[i][j]表示前i个数分成j组,显然该位可以填0的条件是:

  1.存在k在i前面分了j-1组可行;

  2.异或值满足之前已经枚举的相同(保证最小)

  3.这一位可以是0

  这样可以过71分,最后一组数据TLE,发现最后一组数据下界固定为1,由于显然发现组数越小越优,可以令g[i]表示令第i位为0的最小组数,如果组数<B则这位可以为0。

Code

  1 #include<iostream> 
  2 #include<algorithm>  
  3 #include<cstdio>  
  4 #include<cstring>  
  5 #include<cstdlib>  
  6 #include<cmath>  
  7 using namespace std;  
  8      
  9 const int ONE=2001;
 10  
 11 int n,A,B;
 12 int a[ONE],g[ONE];
 13 bool f[ONE][ONE];
 14 long long Sum[ONE];
 15 long long res;
 16 long long total;
 17 int PD,len;
 18  
 19 int get() 
 20 { 
 21         int res,Q=1;    char c;
 22         while( (c=getchar())<48 || c>57)
 23         if(c=='-')Q=-1;
 24         if(Q) res=c-48; 
 25         while((c=getchar())>=48 && c<=57) 
 26         res=res*10+c-48; 
 27         return res*Q; 
 28 }
 29  
 30 void PartOne()
 31 {
 32         for(int pos=len;pos>=1;pos--)
 33         {
 34             memset(f,0,sizeof(f));
 35             f[0][0]=1;
 36              
 37             for(int i=1;i<=n;i++)
 38             for(int j=1;j<=i;j++)
 39             for(int k=0;k<=i-1;k++)
 40             {
 41                 total=Sum[i]-Sum[k];
 42                  
 43                 if(f[k][j-1] && ((total>>pos)|res)==res &&  ((total>>(pos-1)) & (long long)1)==0 )
 44                 {
 45                     f[i][j]=1;
 46                     break;
 47                 }
 48             }
 49              
 50             PD=0;
 51             for(int i=A;i<=B;i++)
 52             {
 53                 PD=f[n][i];
 54                 if(PD) break;
 55             }
 56             res<<=1;
 57             if(!PD) res|=1;
 58         }
 59 }
 60  
 61 void PartTwo()
 62 {
 63         for(int pos=len;pos>=1;pos--)
 64         {
 65             memset(g,63,sizeof(g));
 66             g[0]=0;
 67             for(int i=1;i<=n;i++)
 68             for(int k=0;k<=i-1;k++)
 69             {
 70                 total=Sum[i]-Sum[k];
 71                  
 72                 if(((total>>pos)|res)==res &&  ((total>>(pos-1)) & (long long)1)==0 )
 73                 {
 74                     g[i]=min(g[i],g[k]+1);
 75                 }
 76             }
 77              
 78             res<<=1;
 79             if(g[n]>B) res|=1;
 80         }
 81          
 82 }
 83  
 84 int main()
 85 {      
 86         n=get();    A=get();    B=get();
 87         for(int i=1;i<=n;i++)
 88         {
 89             a[i]=get();
 90             Sum[i]=Sum[i-1]+a[i]; 
 91         }
 92          
 93         total=Sum[n];
 94         while(total)
 95         {
 96             len++;
 97             total>>=1;
 98         }
 99          
100         if(A!=1) PartOne();
101         else PartTwo();
102          
103         printf("%lld",res);
104              
105 }
View Code

 

posted @ 2017-02-21 16:15  BearChild  阅读(362)  评论(0编辑  收藏  举报