poj 1038 Bugs Integrated, Inc. 題解

江北南風發表於2021-01-25

題面

提供一種程式碼難度比較簡單的做法(可能)

狀態表示:

設定狀態$ 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;
}

相關文章