P7689 [CEOI2002] Bugs Integrated,Inc.
题意
一个 行 列的网格,里面可以放 和 两种不同的芯片,其中 个格子被损坏不能占用,求最多可以放下多少个芯片。
分析
注意到 特别小,不超过 ,因此可以考虑使用状态压缩 ,参考炮兵阵地这题在《算法竞赛进阶指南》上的解法二,我们可以设计一种类似的状态表示方式:
- 的芯片的表示方法:
- 的芯片的表示方法:
的芯片的两个 和 的三个 都表示放了一个芯片,规定 后面必须填 , 后面必须填 ,只有 后面才可以放新的芯片,我们称之为“自由格”,记为 ,显然, 的位置可能填 或 或 。
这样,每一列都可以被状态压缩为一个 位三进制数,可以用一个 之间的一个十进制整数表示。
设 表示第 列状态为 时,前 列可以放的芯片个数,若状态不合法则值应为 。我们枚举一个合法的状态 ,把它按三进制位拆分到 中,考虑它可以转移到第 列的哪些状态 ,先求出 的待定数组 ,根据前面的表示方法,有 ,若 已经被损坏,则 ,若 被损坏的同时 ,说明在状态 下 应被占用却是损坏的,要转移到 也是不合法的,应该排除这种情况下的状态 。
然后我们通过待定数组 求出合法转移状态 ,但像这种状态表示比较复杂,冗余比较多的题目,我们不一定非要写出确切的状态转移方程,可以通过深度优先搜索确定可以转移到的 :
- 若 ,则 被强制用为 或为自由格,这个格子可以填 。
- 若 ,则 被强制用为 ,这个格子必须填 。
- 若 ,则 为自由格,则查询接下去的 或 个格子是否也是自由格,若是则可以放芯片,芯片数量增加 。
目标状态为 。
看起来这道题目已经做完了,不过交上去你会发现 了,原来这道题目过于毒瘤,只留了 的空间,于是我们可以使用滚动数组优化空间,不过要注意重置数组(我就是这样调到凌晨 点的)。
这种做法的时间复杂度是 ,十分玄学,会 得死死的,我们可以加一个小优化:注意到枚举状态 时,有一些损坏的格子的状态一定是 ,完全不需要花太多时间去枚举他们,我们记录第 列的损坏的格子的个数 ,只要枚举 个 未损坏的格子的状态即可。
然而这种做法还是过于垃圾,只能得到 分,得加上大量的优化,选择合适的语言,开启 后卡卡常数才可以压线过。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=160,M=12,S=6e4+10;
int d,n,m,k,m3[M]={1},dp[2][S],in[N],a[M],s,b[M];
bool v[N][M];
void make(int x,int j){
s=0;
for(int i=m;i>=1;i--){
if(v[x][i])
a[i]=0;
else{
a[i]=j%3;
j/=3;
}
s=s*3+a[i];
b[i]=a[i]-1;
if(v[x+1][i]&&a[i]){//跳过特殊不合法状态
s=m3[m];
return;
}
if(v[x+1][i])
b[i]=0;
}
}
void dfs(int x,int y,int t,int sum){
if(!y){
dp[x^1][t]=max(dp[x^1][t],dp[x][s]+sum);
return;
}
//填 0
if(b[y]<=0)
dfs(x,y-1,t*3,sum);
//强制填 1
if(b[y]==1)
dfs(x,y-1,t*3+1,sum);
//可以放芯片
if(b[y]==-1){
//3*2
if(y-2>0&&b[y-1]==-1&&b[y-2]==-1)
dfs(x,y-3,t*27+1*9+1*3+1,sum+1);
//2*3
if(y-1>0&&b[y-1]==-1)
dfs(x,y-2,t*9+2*3+2,sum+1);
}
}
int main(){
for(int i=1;i<12;i++)
m3[i]=m3[i-1]*3;
d=read();
while(d--){
n=read();m=read();k=read();
memset(v,0,sizeof(v));
memset(in,0,sizeof(in));
memset(dp,-1,sizeof(dp));
for(int i=1,x,y;i<=k;i++){
x=read();y=read();
v[x][y]=1;
in[x]++;
}
dp[0][0]=0;
for(int i=0;i<n;i++){
for(int j=0;j<m3[m-in[i]];j++){
make(i,j);
if(dp[i&1][s]==-1)//不合法状态
continue;
dfs(i&1,m,0,0);
}
for(int j=0;j<m3[m-in[i+2]];j++){//重置数组
make(i+2,j);
dp[i&1][s]=-1;
}
}
write(dp[n&1][0]);
puts("");
}
return 0;
}

浙公网安备 33010602011771号