【课后服务】20181022切蛋糕

权当抛砖引玉吧,掌握记搜的方法最重要。

#include<iostream>
#include<cstring> 
#include<cstdio>
using namespace std;
int n,m,k;
bool book[21][21];
int cake[21][21];
int dp[21][21][21][21];
int yt(int x,int y,int w,int h)//返回蛋糕内樱桃值;x、y表示左上角坐标,w、h表示长和宽
{
	int yingtao=cake[x+w][y+h]-cake[x+w][y]-cake[x][y+h]+cake[x][y];
	return yingtao;
}
int dfs(int x,int y,int w,int h)//开成int类型
{	
	int ans=2147483647;
	if(dp[x][y][w][h]!=-1) return dp[x][y][w][h];//若x,y,w,h的状态已被搜过,则返回已经保存的结果
	if(yt(x,y,w,h)==1) return 0;//樱桃值为1时,代价为0(不用再切了)
	if(yt(x,y,w,h)==0) return 214748364;//樱桃值为0时,代价无穷大;但在实现时,INF不能大于INT_MAX/2
	for(int i=1;i<w;i++) ans=min(ans,dfs(x,y,i,h)+dfs(x+i,y,w-i,h)+h);//枚举各行
	for(int i=1;i<h;i++) ans=min(ans,dfs(x,y,w,i)+dfs(x,y+i,w,h-i)+w);//枚举各列
	dp[x][y][w][h]=ans;//保存结果
	return ans;
}
int main()
{
	memset(dp,-1,sizeof(dp));//初始化
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		book[x][y]=1;
	}
	for(int i=1;i<=n;i++){//前缀和
		for(int j=1;j<=m;j++){
			cake[i][j]=cake[i-1][j]+cake[i][j-1]-cake[i-1][j-1];
			if(book[i][j]) cake[i][j]++;
		}
	}
	int ans=dfs(0,0,n,m);
	printf("%d",ans);
	return 0;
} 

记忆化搜索,其实就是动态规划(DP)与搜索的完美结合,提高了搜索的效率,也提供了DP的道路。

所谓动态规划,就是动态的规划,其本质就是“状态转移”——从一种情况直接或间接推出其他情况。对于本题而言,就是要想清楚如何得到最小代价,是由哪几部分组成。

so本题具体操作如下:

定义一四维数组dp,存储在x,y,w,h的状态下的最小代价。

定义一int类型函数dfs,其返回值即为代价。若蛋糕已有解,则直接返回现有代价。否则若当前蛋糕内樱桃值为1,返回代价为0。若当前蛋糕内樱桃值为0,返回代价为“无穷大”。否则继续枚举可供下刀的行和列,继续对两侧进行搜索,打擂台寻找最小代价。遍历完所有情况后,保存当前结果并返回上一层。

小结:记忆化搜索与暴搜的最大区别在于,前者对搜索进行了优化,对当前结果进行了记录,避免了重复搜索。

原文地址:https://www.cnblogs.com/dong-ji-yuan/p/9845264.html