[APIO2007]动物园 解题记录
作者:互联网
题目:https://www.luogu.org/problemnew/show/P3622
参考题解:https://qiu.blog.luogu.org/solution-p3622
这题一开始我考虑的是将每个小朋友作为dp的阶段,后来发现这样很难避免前后的冲突,只有45分。
45分代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 const int N=10000; 8 const int M=31+7; 9 const int C=50000+7; 10 void pre(int,int*); 11 void init(); 12 bool check(int,int,int); 13 void run(); 14 int ans,lim=1<<5; 15 int main(){ 16 init(); 17 printf("%d\n",ans); 18 return 0; 19 } 20 int n,c,st[C],h[C],l[C],s[7],ext; 21 void init(){ 22 cin>>n>>c; 23 int h,l,x; 24 for(int i=1;i<=c;i++){ 25 memset(s,0,sizeof(s)); 26 scanf("%d%d%d",&st[i],&h,&l); 27 for(int j=1;j<=h;j++){ 28 scanf("%d",&x); 29 x= x<st[i] ?x+n :x; 30 s[x-st[i]+1]=1; 31 } 32 for(int j=1;j<=l;j++){ 33 scanf("%d",&x); 34 x= x<st[i] ?x+n :x; 35 s[x-st[i]+1]=2; 36 } 37 pre(i,s); 38 } 39 int tail=st[c]+5-1-n; 40 if(tail>0) 41 while(tail>=st[ext+1]) 42 ext++; 43 } 44 int f[C][M],dp[C][M]; 45 bool zero[C][M]; 46 void pre(int u,int *s){ 47 for(int i=0;i<lim;i++){ 48 int t=i,cnt=0; 49 for(int j=1;j<=5;j++){ 50 if(s[++cnt]==(t&1)+1){ 51 f[u][i]=1; 52 if(u==1)dp[u][i]=1; 53 break; 54 } 55 t>>=1; 56 } 57 } 58 } 59 void run(){ 60 for(int j=0;j<lim;j++) 61 zero[1][j]=1; 62 for(int i=2;i<=c+ext;i++){ 63 int l,r,step; 64 if(i>c){ 65 memset(dp[i],0,sizeof(dp[i])); 66 memset(zero[i],0,sizeof(zero[i])); 67 l=i==c+1 ?c :i-1-c; 68 r=i-c; 69 } 70 else l=i-1,r=i; 71 if(i==c+1) step=st[l]+5-(st[r]+n); 72 else step=st[l]+5-st[r]; 73 step=max(step,0); 74 for(int j=0;j<lim;j++){ 75 if(i>c) dp[r][j]=0; 76 for(int k=0;k<lim;k++) 77 if(check(j,k,step)&&dp[l][k]>=dp[r][j]){ 78 dp[r][j]=dp[l][k]; 79 zero[r][j]=(zero[l][k]&&j==0); 80 } 81 if(f[r][j]) dp[r][j]++; 82 } 83 } 84 if(ext)c=ext; 85 for(int i=0;i<lim;i++) 86 if(!zero[c][i]) ans=max(ans,dp[c][i]-ext); 87 } 88 bool check(int a,int b,int step){ 89 if(step==0)return 1; 90 a&=(int)pow(2,step)-1; 91 b>>=5-step; 92 if(a==b)return 1; 93 else return 0; 94 }
后来看了题解,发现应该将每个围栏作为dp的阶段,大概思路如下:
1. 读入,并预处理出 num[i][j](从 i 开始,后5位状态为 j 时,高兴的小朋友的数量)
2. dp,转移方程如下。PS.这里的dp要进行多次(2^5次),每次先确定 i = n 时,后5位的状态,设为 t,并在最后只取 dp[n][t] 的值,就保证了前后不会冲突
f[i][j]=max(f[i-1][(j&15)<<1],f[i-1][(j&15)<<1|1]+num[i][j]
AC代码
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 const int N=10000+7; 7 const int M=32+7; 8 void init(); 9 void run(); 10 int ans; 11 int main(){ 12 //freopen("1.txt","r",stdin); 13 init(); 14 run(); 15 printf("%d\n",ans); 16 return 0; 17 } 18 int n,c,lim=32,st,a,l,x,afraid,love,num[N][M]; 19 void init(){ 20 cin>>n>>c; 21 for(int i=1;i<=c;i++){ 22 scanf("%d%d%d",&st,&a,&l); 23 afraid=love=0; 24 for(int j=1;j<=a;j++){ 25 scanf("%d",&x); 26 int t=(x-st+n)%n; 27 afraid|=1<<t; 28 } 29 for(int j=1;j<=l;j++){ 30 scanf("%d",&x); 31 int t=(x-st+n)%n; 32 love|=1<<t; 33 } 34 for(int j=0;j<lim;j++){ 35 if(~j&afraid || j&love) num[st][j]++; 36 } 37 } 38 } 39 int dp[N][M]; 40 void run(){ 41 for(int t=0;t<lim;t++){ 42 memset(dp,128,sizeof(dp)); 43 dp[0][t]=0; // 相当于先确定最后一位的情况,这样就可以避免前后冲突 44 for(int i=1;i<=n;i++) 45 for(int j=0;j<lim;j++) 46 dp[i][j]=max(dp[i-1][(j&15)<<1],dp[i-1][(j&15)<<1|1])+num[i][j]; 47 ans=max(ans,dp[n][t]); 48 } 49 }
吐槽一下,这道题题面明明说了“不能移走所有动物”,但题解貌似都没有进行判断,而且这样也能过……
标签:int,APIO2007,st,zero,解题,void,include,动物园,dp 来源: https://www.cnblogs.com/BruceW/p/11125426.html