权当抛砖引玉吧,掌握记搜的方法最重要。
#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/2for(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,返回代价为“无穷大”。否则继续枚举可供下刀的行和列,继续对两侧进行搜索,打擂台寻找最小代价。遍历完所有情况后,保存当前结果并返回上一层。
小结:记忆化搜索与暴搜的最大区别在于,前者对搜索进行了优化,对当前结果进行了记录,避免了重复搜索。