2018.12.22【NOIP提高组】模拟B组 JZOJ 3511 cza的蛋糕

版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/85207987

大意

给定一个 n × m n\times m 的矩阵,其中一些点已经被覆盖,现在要用 1 × 2 1\times 2 的长方形去覆盖这个矩阵(可以90度翻转),问放到不能再放的最少长方形放置数。

数据范围:
对于30%的数据 N 5 M 5 N\leq 5,M\leq 5
对于100%的数据 N 70 M 7 N\leq 70, M\leq 7


思路

10%玄学

代码1

#include<algorithm>
#include<cstdio>
using namespace std;int n,m;
signed main()
{
	freopen("cake.in","r",stdin);
	freopen("cake.out","w",stdout);
	scanf("%d%d",&n,&m);
	printf("%d",max(n,m));
}

30%暴搜

代码2

#include<cstdio>
#include<iostream>
using namespace std;int n,m,ans=0x3f3f3f3f;
char c;
bool ok[71][9];
inline char Getchar()
{
    static char buf[100000],*p1=buf+100000,*pend=buf+100000;
    if(p1==pend)
	{
        p1=buf; pend=buf+fread(buf,1,100000,stdin);
        if (pend==p1) return -1;
    }
    return *p1++;
}
inline bool check()
{
	for(register int i=1;i<=n;i++)
	 for(register int j=1;j<=m;j++)
	 if(!ok[i][j])
	  if(!ok[i+1][j]||!ok[i][j+1]) return false;
	return true;
}
inline void dfs(register int now,register int x,register int y)
//在线的 过程 深度优先搜索 (记录整型 当前使用的长方形个数,记录整型 当前是第几行,记录整型 当前是第几个)
{
	if(now>=ans) return;//最优性剪枝
	if(x==n&&y==m)
	{
		if(check())ans=now;//保存答案
		return;
	}
	if(ok[x][y])//已被覆盖
	{
		if(y==m) dfs(now,x+1,1);//下一行
		else dfs(now,x,y+1);//下一个
		return;
	}
	ok[x][y]=true;//填充
	if(!ok[x][y+1]&&y<m)//后面那格可以填
	{
		ok[x][y+1]=true;//填
		dfs(now+1,x,y+1);
		ok[x][y+1]=false;//回溯
	}
	if(!ok[x+1][y]&&x<n)//下面那格可以填
	{
		ok[x+1][y]=true;//填
		if(y==m) dfs(now+1,x+1,1);
		else dfs(now+1,x,y+1);//判断是否填到边界
		ok[x+1][y]=false;
	}
	ok[x][y]=false;
	if(ok[x-1][y]||ok[x][y-1])//可以空
	{
		if(y==m) dfs(now,x+1,1);//直接空掉这格
			else dfs(now,x,y+1);
	}
}
signed main()
{
	freopen("cake.in","r",stdin);
	freopen("cake.out","w",stdout);
	cin>>n>>m;
	for(register int i=1;i<=n;i++)
	{
		ok[i][0]=ok[i][m+1]=1;
		for(register int j=1;j<=m;j++)
		{
			cin>>c;
			ok[0][j]=ok[n+1][j]=1;//周围不能再填
			if(c=='*') ok[i][j]=1;//标记
		}
	}
	dfs(0,1,1);//搜索
	cout<<ans;
}

我们发现这样的代码没有记忆化,于是乎我们就想到了记忆化,但是这样由于状态很多没法表示,于是就可以用状态压缩每一行,然后进行状压 d p dp (记忆化搜索)

AC代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
using namespace std;
int p[11],f[71][131][131],n,m;
char c;
int i,j,l,a[72],s,ans=0x3f3f3f3f;
inline void cge(ri k,ri x, ri y,ri z,ri t)
//在线的 过程 修改(当前填补的列数,上一行的状态,这一行的状态,下一行的状态,当前步数)
{
	if(k>0&&((x&p[k-1])==0)&&((y&p[k-1])==0)) return;//若出现纵的两个空位,不合法,退出
	if(k>1&&((y&p[k-1])==0)&&((y&p[k-2])==0)) return;//若出现横的两个空位,不合法,退出
	if(k==m)//搜索完毕
	{
		f[i][y][z]=min(f[i][y][z],f[i-1][j][l]+t);//状态转移
		return;
	}
	cge(k+1,x,y,z,t);//这一格不填
	if(((z&p[k])==0)&&((y&p[k])==0))//能填纵的
	cge(k+1,x,y|p[k],z|p[k],t+1);//这一行的和下一行的都要修改
	if(k<m-1&&((y&p[k])==0)&&((y&p[k+1])==0))//能填横的
	cge(k+2,x,y|p[k]|p[k+1],z,t+1);//修改这一行的两个值
}
signed main()
{
	freopen("cake.in","r",stdin);
	freopen("cake.out","w",stdout);
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		int x=0;
		for(j=1;j<=m;j++)
		{
			cin>>c;
			x<<=1;
			if(c=='*') x++;
		}
		a[i]=x;
	}
	memset(f,0x3f3f3f3f,sizeof(f));
	p[0]=1;
	for(i=1;i<10;i++) p[i]=p[i-1]<<1;
	s=p[m]-1;
	f[0][s][a[1]]=0;
	for(i=1;i<=n;i++)
	for(j=0;j<=s;j++)
	for(l=0;l<=s;l++)
	if(f[i-1][j][l]<0x3f3f3f3f)
	cge(0,j,l,a[i+1],0);
	for(i=0;i<=s;i++)
	ans=min(ans,f[n][i][0]);
	cout<<ans;
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/85207987