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.