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 }
正解应该枚举栅栏,这样移位就是固定的
关键点:环的处理,状态里每一位的顺序(从右到左)
处理环就是在外面再套一层循环,枚举起点的状态,最后只有状态和起点相同才合法,所以只要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 }
总结一下大体思路是对的,只不过重要的细节处理有时候自己还是一个人想不清楚,慢慢琢磨琢磨也就好了,不要依赖题解
一点一点的进步
予明日所有失败者 赋万千不灭颂歌

浙公网安备 33010602011771号