1. 程式人生 > 實用技巧 >習題:Clear The Matrix(狀壓DP)

習題:Clear The Matrix(狀壓DP)

題目

傳送門

思路

對於所有的變化,我們發現最多隻有4*4

考慮\(dp[i][j]\)表示前i個數,i,i-1,i-2,i-3的狀態為j的最小方案數

對於轉移而言,其實我們只需要將所有的操作矩陣的左上角保證在i-3行即可

感覺這道題更像一個暴力

程式碼

#include<iostream>
#include<cstring>
using namespace std;
int n;
int w[5];
int f[1005];
int dp[1005][(1<<16)];
//第i列,壓i,i+1,i+2,i+3列
int nxt[5][5];
//第i行放一個長度為j的矩陣
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=4;i++)
		cin>>w[i];
	for(int i=1;i<=4;i++)
	{
		for(int j=1;j<=n;j++)
		{
			char c;
			cin>>c;
			f[j]<<=1;
			if(c=='*')
				f[j]++;
		}
	}
	for(int i=1;i<=4;i++)
	{
		for(int j=1;i+j-1<=4;j++)
		{
			int now=0;
			int init[5][5]={};
			for(int a=i;a<=i+j-1;a++)
				for(int b=1;b<=j;b++)
					init[a][b]=1;
			for(int j=1;j<=4;j++)
				for(int i=1;i<=4;i++)
					now=(now<<1)+(!init[i][j]);
			nxt[i][j]=now;
		}
	}
	int bas=0;
	for(int i=1;i<=4;i++)
	{
		bas<<=4;
		bas|=f[i];	
	}
	memset(dp,0x3f,sizeof(dp));
	//cout<<dp[1][bas]<<endl;
	dp[1][bas]=0;
	//cout<<dp[1][bas]<<endl;
	for(int i=1;i<=n+1;i++)
	{
		for(int j=(1<<16)-1;j>=0;j--)
		{
			if(dp[i][j]==dp[0][0])
				continue;
			if((j>>12)==0)
			{
				int now=(j<<4)|(f[i+4]);
				dp[i+1][now]=min(dp[i+1][now],dp[i][j]);
			}
			for(int a=1;a<=4;a++)
				for(int b=1;a+b-1<=4&&i+b-1<=n;b++)
					dp[i][j&nxt[a][b]]=min(dp[i][j&nxt[a][b]],dp[i][j]+w[b]);
		}
	}
	cout<<dp[n+1][0];
	return 0;
}