记忆化搜索专题训练

记忆化搜索

算法上依然是搜索的流程,但是搜索到的一些解,用动态规划的那种思想和模式作一些保存。一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。
这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。—[百度百科]
XM

POJ 1088 滑雪

链接

滑雪

题意

有一个R*C的矩形区域,区域(i,j)的高度为h[i,j]
一个人可以从某个点滑向上下左右相邻四个点之一,
当且仅当高度减小,求最长区域的长度

分析

记忆化搜索
若与点a相邻的点b高度比a大,则从b可以到a
dp[i][j]为以(i,j)为起点最长区域的长度
dp[i][j]=max(上下左右四个点能到(i,j)的长度)+1

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<stdio.h>
#include<algorithm>
using namespace std;
int h[105][105],dp[105][105],r,c;
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,-1,1};
bool judge(int row,int col)
{

if(row>=1&&row<=r&&col>=1&&col<=c)
return true;
return false;
}
int dfs(int x,int y)
{

if(dp[x][y])
return dp[x][y];
dp[x][y]=1;
for(int k=0;k<4;k++){
int row=x+dx[k];
int col=y+dy[k];
if(judge(row,col)&&h[x][y]>h[row][col])
dp[x][y]=max(dp[x][y],dfs(row,col)+1);
}
return dp[x][y];
}
int main()
{

scanf("%d%d",&r,&c);
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++){
scanf("%d",&h[i][j]);
dp[i][j]=0;
}
int ans=-1;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
ans=max(ans,dfs(i,j));
printf("%d\n",ans);
return 0;
}

Hdu 1078 FatMouse and Cheese

链接

FatMouse and Cheese

题意

有n*n个洞,每个洞都藏有一定量的奶酪,有一只老鼠初始在(0,0)点
它每次可以从所在地A水平或垂直方向最多走k步到达B,且B的奶酪值比A大
它可以吃掉B处的奶酪,求它最多可以吃多少奶酪

分析

记忆化搜索
dp[i][j]为从(0,0)点出发到(i,j)点能吃的最大奶酪值,则(i,j)可由四个方向到达
因为起点为(0,0)点,但终点未知,所以可以以(0,0)点为终点逆向dfs
注:每次必须朝水平或垂直一个方向走最多k步,而不是每次可以拐弯

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=105;
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
int n,k,edge[N][N],dp[N][N];
bool judge(int row,int col)
{

if(row>=1&&row<=n&&col>=1&&col<=n)
return true;
return false;
}
int dfs(int x,int y)
{

if(dp[x][y])
return dp[x][y];
int ans=0;
for(int i=1;i<=k;i++){ //可以走1~k步
for(int j=0;j<4;j++){
int row=x+dx[j]*i; //记得*i
int col=y+dy[j]*i;
if(judge(row,col)&&edge[x][y]<edge[row][col])
ans=max(ans,dfs(row,col));
}
}
dp[x][y]=ans+edge[x][y];
return dp[x][y];
}
int main()
{

while(scanf("%d%d",&n,&k)!=EOF){
if(n==-1&&k==-1)
break;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&edge[i][j]);
memset(dp,0,sizeof(dp));
dfs(1,1);
printf("%d\n",dp[1][1]);
}
return 0;
}

Hdu 1428 漫步校园

链接

漫步校园

题意

有一个n*n的方格,走到每个方格都需要一定时间
先要从(0,0)点走到(n,n)点,每次可以走到上下左右相邻的点
若从A点到B点,则从B出发存在一条到终点的路比从A出发的任意一条到终点的路都近
求从(0,0)点走到(n,n)点有多少条路

分析

本题实质是求从起点到终点的最短路的条数
可以先用SPFA求以(n,n)点为原点到各点的最短路径
再用记忆化搜索逆向求(n,n)点到(0,0)点的最短路径长度
正向求(0,0)到(n,n)TLE了,不懂为什么…

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<stdio.h>
#include<queue>
#include<string.h>
const int N=60;
const int INF=999999;
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
using namespace std;
int n,dis[N][N],edge[N][N];
long long dp[N][N];
bool judge(int row,int col)
{

if(row>=1&&row<=n&&col>=1&&col<=n)
return true;
return false;
}
void SPFA()
{

bool vis[N][N];
queue<int> q;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis[i][j]=INF;
vis[i][j]=false;
}
}
dis[n][n]=edge[n][n];
q.push(n);
q.push(n);
vis[n][n]=true;
while(!q.empty()){
int x=q.front();
q.pop();
int y=q.front();
q.pop();
vis[x][y]=false;
for(int k=0;k<4;k++){
int row=x+dx[k];
int col=y+dy[k];
if(judge(row,col)&&dis[x][y]+edge[row][col]<dis[row][col]){
dis[row][col]=dis[x][y]+edge[row][col];
if(!vis[row][col]){
q.push(row);
q.push(col);
vis[row][col]=true;
}
}
}
}
}
long long dfs(int x,int y)
{

if(dp[x][y])
return dp[x][y];
for(int k=0;k<4;k++){
int row=x+dx[k];
int col=y+dy[k];
if(judge(row,col)&&dis[row][col]<dis[x][y])
dp[x][y]+=dfs(row,col);
}
return dp[x][y];
}
int main()
{

while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&edge[i][j]);
SPFA();
memset(dp,0,sizeof(dp));
dp[n][n]=1;
dfs(1,1);
printf("%I64d\n",dp[1][1]);
}
return 0;
}