题意:
给你n*m的方格(1<=n<=150,1<=m<=10)其中有k个坏点。现在要用2*3的矩行填充方格,问最大填充数量。
分析:
很明显m才10所以我们可以压缩状态
我们令dp【i】【j】表示第i行状态为j的时候最多能放多少个方格;
我们是逐格递推的那么每一次我们都假设当前格子为矩形的左下角,那么就是下面2种情况
红色就是我们当前扫描到的格子
对于每个格子有3种状态:
第一种上方的前2格都未被占据那么记作2
第二种上方第二格被占据记作1
第三种上方前一格被占据记作0
我们用per数组记录上一行的每一个格子的状态
我们用cur数组记录当前行的每一个格子的状态
那么如果当前可以放置矩形的话要满足下面的条件
我们用per数组记录上一行的每一个格子的状态
因为j=3^10所以j*150很大又因为dp【i】【j】只和上一行状态有关所以可以用滚动数组
ACcode:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 59050
#include <cstring>
using namespace std;
int tri[]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};
int mapp[151][11];
int dp[2][maxn];
int per[11],cur[11];
int n,m,pass;
int ten(int *p){
int res=0;
for(int i=1;i<=m;++i)res+=p[i]*tri[i];
return res;
}
void three(int t){
for(int i=1;i<=m;++i,t/=3)per[i]=t%3;
}
void dfs(int i,int j,int cnt,int stata){
int k;
dp[i%2][stata]=max(dp[i%2][stata],cnt);
if(j>=m)return;
if(!per[j]&&!per[j+1]&&!cur[j]&&!cur[j+1]){///3*2
cur[j]=cur[j+1]=2;
k=ten(cur);
dfs(i,j+2,cnt+1,k);
cur[j]=cur[j+1]=0;
}
if(j<m-1&&!cur[j]&&!cur[j+1]&&!cur[j+2]){///2*3
cur[j]=cur[j+1]=cur[j+2]=2;
k=ten(cur);
dfs(i,j+3,cnt+1,k);
cur[j]=cur[j+1]=cur[j+2]=0;
}
dfs(i,j+1,cnt,stata);
}
int main(){
int loop,k,stata,ans;
scanf("%d",&loop);
while(loop--){
memset(dp,-1,sizeof(dp));
memset(mapp,0,sizeof(mapp));
scanf("%d%d%d",&n,&m,&k);
while(k--){
int x,y;
scanf("%d%d",&x,&y);
mapp[x][y]=1;
}
for(int i=1;i<=m;++i)per[i]=mapp[1][i]+1;
stata=ten(per);
dp[1][stata]=0;
for(int i=2;i<=n;++i){
for(int j=0;j<tri[m+1];++j)dp[i%2][j]=-1;
for(int j=0;j<tri[m+1];++j){
if(dp[(i+1)%2][j]==-1)continue;
three(j);
for(int l=1;l<=m;++l)
if(mapp[i][l])cur[l]=2;
else cur[l]=max(per[l]-1,0);
pass=dp[(i+1)%2][j];
dfs(i,1,pass,ten(cur));
}
}
ans=0;
for(int i=0;i<tri[m+1];++i)ans=max(ans,dp[n%2][i]);
printf("%d\n",ans);
}
return 0;
}