權當拋磚引玉吧,掌握記搜的方法最重要。
#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,返回代價為“無窮大”。否則繼續枚舉可供下刀的行和列,繼續對兩側進行搜索,打擂臺尋找最小代價。遍歷完所有情況后,保存當前結果并返回上一層。
小結:記憶化搜索與暴搜的最大區別在于,前者對搜索進行了優化,對當前結果進行了記錄,避免了重復搜索。