[BZOJ1151][CTSC2007]动物园zoo 解题报告|DP|位运算

Description

 

 

 

  最近一直在为了学习算法而做题,这道题是初一小神犇让我看的。感觉挺不错于是写了写。

  这道题如果是一条线的话我们可以构造一个DP f[i,j]表示以i为起点,i,i+1...i+4的取与不取的状态的二进制为j然后1~i积累的答案

  以前几乎没有这么写过,因为很难想到j是没有后效性的

  前一个状态有两种情况,i-1位取,i-1位不取

  也就是f[i-1,j >> 1] f[i-1,j >> 1+1 << 4]

  然后小朋友要怎么处理才能做到不重复不遗漏呢

  答案是记录以每个点为起点,连着5位的状态为j时从那个点出发的小朋友的开心个数

  显然当我们知道连着5位的状态时,完全可以推出该小朋友开不开心

  这一部分很容易脑补 但是真正写起来用位运算比较方便

  而i-1位是否取对当前也是没有影响的,所以这个DP就可以敲起来啦

  

  但是如何处理环形?

  固定前4位的取与不取的状态就可以啦...

  最后的时间复杂度就是DP状态的O(2^5*C)

  处理环的代价是O(2^4)

  最后是O(2^9*C) C<=5*10^5

  对于10s的时限来说还是可以过哒~

  1 /**************************************************************
  2     Problem: BZOJ1151
  3     Author: mjy0724
  4     Time:3760 ms
  5     Memory:30336 kb
  6 ****************************************************************/
  7 
  8 program bzoj1151;
  9 const maxn=100100;
 10 var i,j,x,n,c:longint;
 11     e,h,l,next,link:array[-1..maxn]of longint;
 12     hate,love:array[-1..maxn,-1..6]of boolean;
 13     num,f:array[-1..maxn,-1..32]of longint;
 14 
 15 function max(a,b:longint):longint;
 16 begin
 17     if a>b then exit(a) else exit(b);
 18 end;
 19 
 20 procedure solve_num;
 21 var i,j,k,t:longint;
 22 begin
 23     fillchar(num,sizeof(num),0);
 24     for i:=1 to n do
 25         for j:=0 to 31 do
 26         begin
 27             k:=link[i];
 28             while k<>0 do
 29             begin
 30                 for t:=1 to 5 do if (j and (1 << (5-t)))<>0 then
 31                 //这个位运算刚开始一直出错,不能写成=1因为and之后虽然只有1位是1但是不一定在最后一位QAQ
 32                 begin
 33                     if love[k,t] then
 34                     begin
 35                         inc(num[i,j]);break;//这里的break很重要 因为1个小朋友只能算一次
 36                     end;
 37                 end else
 38                 begin
 39                     if hate[k,t] then
 40                     begin
 41                         inc(num[i,j]);break;
 42                     end;
 43                 end;
 44                 k:=next[k];
 45             end;
 46         end;
 47 end;
 48 
 49 procedure dp;
 50 var head,i,j,tot,k,ans:longint;
 51 begin
 52     ans:=0;
 53     for head:=0 to 15 do//枚举前4位的状态
 54     begin
 55         fillchar(f,sizeof(f),192);
 56         fillchar(f[0],sizeof(f[0]),0);
 57         for i:=1 to 4 do
 58             for j:=(head << i) and 31 to (head << i) and 31+1 << i-1 do //and 31是小小的位运算技巧,把前面的头都去掉啦
 59                 f[i,j]:=max(f[i-1,j >> 1],f[i-1,j >> 1+1 << 4])+num[i,j];
 60 
 61         for i:=5 to n-4 do
 62             for j:=0 to 31 do
 63                 f[i,j]:=max(f[i-1,j >> 1],f[i-1,j >> 1+1 << 4])+num[i,j];
 64         tot:=0;
 65         for i:=n-3 to n do
 66         begin
 67             inc(tot);
 68             for k:=0 to 1 << (5-tot)-1 do
 69             begin
 70                 j:=k << tot+head >> (n-i);
 71                 f[i,j]:=max(f[i-1,j >> 1],f[i-1,j >> 1+1 << 4])+num[i,j];
 72             end;
 73         end;
 74 
 75         for i:=0 to 31 do ans:=max(ans,f[n,i]);
 76     end;
 77     writeln(ans);
 78 end;
 79 
 80 begin
 81     readln(n,c);
 82     fillchar(hate,sizeof(hate),false);
 83     fillchar(love,sizeof(love),false);
 84         for i:=1 to c do
 85     begin
 86         read(e[i],h[i],l[i]);
 87         for j:=1 to h[i] do
 88         begin
 89             read(x);
 90                 if x>=e[i] then hate[i,x-e[i]+1]:=true else hate[i,(x+n-e[i]) mod n+1]:=true;//这里处理环的情况 表示第i个小朋友视野里的第几个是否讨厌
 91         end;
 92         for j:=1 to l[i] do
 93         begin
 94             read(x);
 95                 if x>=e[i] then love[i,x-e[i]+1]:=true else love[i,(x+n-e[i]) mod n+1]:=true;
 96         end;
 97         readln;
 98             next[i]:=link[e[i]];link[e[i]]:=i;
 99     end;
100        solve_num;
101        dp;
102 end.
原文地址:https://www.cnblogs.com/mjy0724/p/4421333.html