提供一種程式碼難度比較簡單的做法(可能)
狀態表示:
設定狀態$ f[i][j] $,表示第 \(i\) 行狀態為 \(j\) 的最大放置數,因為這是個陰間題,因為題目記憶體設定很小,所以要用滾動陣列,儲存兩行的狀態就夠了。
狀態用三進製表示:
0 表示當前行和上一行均可用
1 表示當前行可用,上一行不可用
2 表示當前行和上一行都不可用
為了方便不過常數確實略大,我們用兩個陣列 \(pre\) , \(now\) 分別存放上一行和當前行的狀態
那麼就有:
inline int getstate(int a[])//存放狀態
{
int temp=0;
for(register int i=1;i<=m;i++)
temp+=a[i]*t[i];
return temp;
}
inline int getstring(int temp,int*a)//取出狀態
{
for(register int i=1;i<=m;i++)
{
a[i]=temp%3;
temp/=3;
}
}
判斷放矩形(設要放矩形的左下角為 (i,j) ):
橫放:
state(i,j)=0 且 state(i,j+1)=0 且 state(i,j+2)=0 \(\ \ \ \ (j+2 \le m\),廢話\()\)
豎放:
state(i,j)=0 且 state(i,j+1)=0 且 state(i-1,j)=0 且 state(i-1,j+1)==0 \(\ \ \ (j+1 \le m\),二度廢話\()\)
DP過程:
顯然,當我們知道第i行狀態,和第i-1行初始狀態時,就可以愉快地DP了。
因為狀態轉換略顯複雜寫成迴圈的話可能需要很多層,所以可以用大法師\(DFS\)來寫。
設定dfs引數分別為,試圖放置的座標,放置矩形的數量,第\(i\)行狀態:
如果足橫放,豎放,不放的情況,分別搜一下就行。
Code:
#include<bits/stdc++.h>
using namespace std;
int n,m,k,D;
int mp[151][15];
int f[2][59050];//f[i][j] i是滾動陣列,j存放狀態
int now[15],pre[15],t[15];//pre為i-1行,now為第i行
//0 表示當前行和上一行均可用
//1 表示當前行可用,上一行不可用
//2 表示當前行和上一行都不可用
inline int qr()
{
char a=0;int w=1,x=0;
while(a<'0'||a>'9'){if(a=='-')w=-1;a=getchar();}
while(a<='9'&&a>='0'){x=(x<<3)+(x<<1)+(a^48);a=getchar();}
return x*w;
}
inline int getstate(int a[])//存放狀態
{
int temp=0;
for(register int i=1;i<=m;i++)
temp+=a[i]*t[i];
return temp;
}
inline int getstring(int temp,int*a)//取出狀態
{
for(register int i=1;i<=m;i++)
{
a[i]=temp%3;
temp/=3;
}
}
void dfs(int i,int j,int num,int state)//(i,j)為嘗試放矩形的右下角位置,num為放矩形數量,state為第i行狀態
{
f[i%2][state]=max(f[i%2][state],num);//更新最大值
int k;
if(j>=m)
return ;
if(j<m-1&&now[j]==0&&now[j+1]==0&&now[j+2]==0)//這一行加上一行能橫放一個
{
now[j]=now[j+1]=now[j+2]=2;
k=getstate(now);
dfs(i,j+3,num+1,k);
now[j]=now[j+1]=now[j+2]=0;
}
if(j<m&&pre[j]==0&&pre[j+1]==0&&now[j]==0&&now[j+1]==0)//這一行和上兩行能豎放一個
{
now[j]=now[j+1]=2;
k=getstate(now);
dfs(i,j+2,num+1,k);
now[j]=now[j+1]=0;
}
dfs(i,j+1,num,state);//不放矩形
}
int main()
{
D=qr();
t[1]=1;
for(register int i=2;i<=11;i++)
t[i]=t[i-1]*3;
while(D--)
{
n=qr();
m=qr();
k=qr();
memset(mp,0,sizeof(mp));
while(k--)
mp[qr()][qr()]=1;
for(register int i=0;i<t[m+1];i++)
f[1][i]=-1;
for(register int i=1;i<=m;i++)//第一行的上一行無法利用
pre[i]=mp[1][i]+1;//記錄初始狀態
int state=getstate(pre);
f[1][state]=0;
for(register int i=2;i<=n;i++)
{
for(register int j=0;j<t[m+1];j++)//預處理第i行狀態
f[i%2][j]=-1;
for(register int j=0;j<t[m+1];j++)
{
if(f[(i+1)%2][j]==-1)
continue;
getstring(j,pre);//取出狀態j
for(register int k=1;k<=m;k++)
{
if(mp[i][k]==1)//這個塊壞了這一行跟上一行都用不了
now[k]=2;
else//塊沒壞
if(pre[k]==2)//上一行用不了
now[k]=1;//只能用本行
else
now[k]=0;//本行和上一行都能用
}
state=getstate(now);
dfs(i,1,f[(i+1)%2][j],state);//從j狀態轉移到state狀態
}
}
int ans=0;
for(register int i=0;i<t[m+1];i++)//統計答案
ans=max(ans,f[n%2][i]);
printf("%d\n",ans);
}
return 0;
}