APIO 2007 动物园 题解

链接题面

看清楚找到小数据范围,第一维表示遍历到的栅栏,第二维是五位状态

先预处理每个状态会使多少小朋友高兴

方程是  f[i][j]=max(f[(i&((1<<4)-1))<<1][j-1],f[((i&((1<<4)-1))<<1)+1][j-1])+b[j][i]

简单点就是  f[i][j]=max(f[(i&15)<<1][j-1],f[((i&15)<<1)+1][j-1])+b[j][i]

一开始想的枚举小朋友,每次转移状态时移的位数不确定,这样有55分,缺点是不能方便处理环,有55分(想调应该也行,不过我调的tle了,我太菜了

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int a[50005][6];int b[50005][1<<5];
 4 int head[50005];
 5 int f[1<<5][50005];
 6 int main()
 7 {
 8    int n,c;
 9    cin>>n>>c;
10    for(int i=1;i<=c;i++)
11    {
12      int e,f,l;
13      scanf("%d%d%d",&e,&f,&l);
14      head[i]=e;
15      for(int j=1;j<=f;j++)
16      {
17         int x;scanf("%d",&x);
18         if(x<e)x+=n;
19         a[i][x-e+1]=-1; 
20      }  
21      for(int j=1;j<=l;j++)
22      {
23         int x;scanf("%d",&x);
24         if(x<e)x+=n;
25         a[i][x-e+1]=1;     
26      }    
27      for(int j=0;j<=(1<<5)-1;j++)
28        for(int k=1;k<=5;k++)
29         if((((j>>(k-1))&1)&&(a[i][k]==1))||((!((j>>(k-1))&1))&&(a[i][k]==-1)))
30          b[i][j]=1;
31    }
32    for(int j=0;j<=c;j++)
33     for(int i=0;i<=(1<<5)-1;i++)
34     {
35         int k=head[j+1]-head[j];
36         for(int p=0;p<=(1<<5)-1;p++)
37         {
38             if((p&((1<<(5-k))-1))!=(i>>k))continue;
39             f[p][j+1]=max(f[p][j+1],f[i][j]+b[j+1][p]); 
40         }
41     }
42     int ans=0;
43     for(int i=1;i<=(1<<5)-1;i++)
44       ans=max(ans,f[i][c]);
45     cout<<ans;
46 } 
View Code

正解应该枚举栅栏,这样移位就是固定的

关键点:环的处理,状态里每一位的顺序(从右到左)

处理环就是在外面再套一层循环,枚举起点的状态,最后只有状态和起点相同才合法,所以只要f[l][n],还有就是每次枚举只有f[l][0]是合法的,别的不能用来转移,赋值负无穷

二进制存数是最右面的是第一位,每次状态转移是要i的上一个状态的后四位推到i,这里倒回去,故i的上一位是i的前四位再加上一个0或1,注意右边是前,左边是后,位运算不要弄反

觉得初始化没啥难的,就是把读入存起来if判断就行了,满足其一就能让他高兴,由于小朋友的视野存在完全重叠(样例2)的可能,一个状态可能让多个小朋友高兴,由于我们已经破环为链,没有后效性

AC代码,时间也可以

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int a[50005][6];int b[50005][1<<5];
 4 int head[50005];
 5 int f[1<<5][50005];
 6 int main()
 7 {
 8    int n,c;
 9    cin>>n>>c;
10    for(int i=1;i<=c;i++)
11    {
12      int e,f,l;
13      scanf("%d%d%d",&e,&f,&l);
14      head[i]=e;
15      for(int j=1;j<=f;j++)
16      {
17         int x;scanf("%d",&x);
18         if(x<e)x+=n;
19         a[i][x-e+1]=-1; 
20      }  
21      for(int j=1;j<=l;j++)
22      {
23         int x;scanf("%d",&x);
24         if(x<e)x+=n;
25         a[i][x-e+1]=1;     
26      }    
27      for(int j=0;j<=(1<<5)-1;j++)
28      {
29          for(int k=1;k<=5;k++)
30         if((((j>>(k-1))&1)&&(a[i][k]==1))||((!((j>>(k-1))&1))&&(a[i][k]==-1)))
31          {b[e][j]++;k=6;}
32      }
33    }
34    int ans=0;
35    for(int l=0;l<=(1<<5)-1;l++)
36    {
37        for(int j=0;j<=(1<<5)-1;j++)f[j][0]=-99999999;
38        f[l][0]=0;
39     for(int j=1;j<=n;j++)
40     for(int i=0;i<=(1<<5)-1;i++)
41     f[i][j]=max(f[(i&15)<<1][j-1],f[((i&15)<<1)+1][j-1])+b[j][i];
42     ans=max(ans,f[l][n]);
43    } 
44     cout<<ans;
45     return 0;
46 } 
View Code

 总结一下大体思路是对的,只不过重要的细节处理有时候自己还是一个人想不清楚,慢慢琢磨琢磨也就好了,不要依赖题解

 一点一点的进步

 

posted @ 2021-04-29 18:57  D'A'T  阅读(142)  评论(2)    收藏  举报