zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    从 java 代码到 android 进程的详细过程
    c++ 智能指针
    linux 进程间共享内存示例
    visual studio 在windows远程调试 linux 程序 cout 输出乱码
    wcf restful 访问报错 *.svc HTTP error 404.17
    c++ 创建 uuid guid
    c++ 事件回调 java
    java通过jna调用so
    java执行jar包
    java调用com组件com4j
  • 原文地址:https://www.cnblogs.com/BearChild/p/6424673.html
Copyright © 2011-2022 走看看