hdu1428 记忆化搜索(BFS预处理最短路径和+DP+DFS)

题意:有一块 n * n 大小的方形区域,要从左上角 (1,1)走到右下角(n,n),每个格子都有通过所需的时间,并且每次所走的下一格到终点的最短时间必须比当前格子走到重点的最短时间短,问一共有多少种走法。

这道题还是很明显的 DP 的,而且鉴于走到相邻格点可以上下左右走,所以我很快就锁定了记忆化搜索这种 DP 方式,但是事实上我的思路大方向的确没有错误,但是我仍然没有很好地挖掘题目的信息。我的想法是,某点到结尾的最短距离我可以转化成到起始点的最短距离,这样我就能从开头的点开始遍历,并且在遍历的时候,如果前后左右有点可以被当前的点优化(即从当前点走到下个点所花总时间比下个点上原本的最短时间还要短,那么我就用当前点优化下一个点,并继续从下一个点 DFS ),我觉得我想的挺对的,而且事实上我认为我的做法本身是可以解这题的,但是仍然会超时。

恩,其实到这里,问题都很明显了,记忆化搜索原本就是很有效地解决超时的一种手段,我之所以超时是因为我用的根本就不是记忆化搜索,而是很普通的 DPS ,DP 数组也只是为了便于传递状态而不是用于记忆,归根结底是我对记忆化搜索的认识还不够,我并没有掌握它,更没有理解记忆究竟是怎么样的,这之前写的两道记忆化搜索的题目其实都有一个共同点,用 DP 数组记录最优解,当再次遍历到这个点的时候就能够直接输出这个 DP 值,而在图中上下左右走根本只是一个题目的出现方式而已。

对题目的理解不到位也是很严重的一个问题,在本题中,已经很清楚地说明了下一个格子到终点的最短时间要比这一个格子到终点的最短时间短,很明显,我只要现处理出每一个点到终点的最短时间,这道题就和之前做的 hdu 1078 几乎一样了。

预处理出最短路径的方法我用的是 BFS ,从终点开始,每当周围有某个点可以用当前点优化,就将它优化后放回队列继续执行操作,当处理出最短路径之后,再用记忆化搜索的方式对整个图 DFS 一遍,用 DP 数组记录每个点到终点共有多少条路可以走,这样就可以得到答案了。

首先是我 TLE 的纯 DFS 代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define ll long long
 4 
 5 int n;
 6 ll dp[51][51],m[51][51],t[51][51];
 7 int xx[4]={1,-1,0,0};
 8 int yy[4]={0,0,1,-1};
 9 
10 void dfs(int x,int y){
11     int i,j;
12     for(i=0;i<4;i++){
13         int dx=x+xx[i],dy=y+yy[i];
14         if(dx>=1&&dx<=n&&dy>=1&&dy<=n){
15             if(dp[dx][dy]==-1||(dp[dx][dy]>dp[x][y]+t[dx][dy])){
16                 dp[dx][dy]=dp[x][y]+t[dx][dy];
17                 m[dx][dy]=m[x][y];
18                 dfs(dx,dy);
19             }
20             else if(dp[dx][dy]==dp[x][y]+t[dx][dy]){
21                 m[dx][dy]=0;
22                 for(j=0;j<4;j++){
23                     int ddx=dx+xx[j],ddy=dy+yy[j];
24                     if(ddx>=1&&ddx<=n&&ddy>=1&&ddy<=n&&(dp[ddx][ddy]+t[dx][dy]==dp[dx][dy])){
25                         m[dx][dy]+=m[ddx][ddy];
26                     }
27                 }
28                 
29                 dfs(dx,dy);
30             }
31         }
32     }
33 }
34 
35 int main(){
36     while(scanf("%d",&n)!=EOF){
37         int i,j;
38         memset(dp,-1,sizeof(dp));
39         memset(m,0,sizeof(m));
40         for(i=1;i<=n;i++){
41             for(j=1;j<=n;j++){
42                 scanf("%I64d",&t[i][j]);
43             }
44         }    
45         dp[1][1]=t[1][1];
46         m[1][1]=1;
47         dfs(1,1);/*
48         printf("dp:
");
49         for(i=1;i<=n;i++){
50             for(j=1;j<=n;j++){
51                 printf("%d ",dp[i][j]);
52             }
53             printf("
");
54         }
55         printf("m:
");
56         for(i=1;i<=n;i++){
57             for(j=1;j<=n;j++){
58                 printf("%d ",m[i][j]);
59             }
60             printf("
");
61         }*/
62         printf("%I64d
",m[n][n]);
63     }
64     return 0;
65 }
View Code

然后是我用 BFS 处理最短路径再记忆化搜索的代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<queue>
 4 using namespace std;
 5 #define ll long long
 6 
 7 int n,c;
 8 
 9 struct point{
10     int x,y;
11 };
12 
13 ll m[51][51],dp[51][51];
14 int t[51][51];
15 int xx[4]={1,-1,0,0};
16 int yy[4]={0,0,1,-1};
17 bool in[51][51];
18 
19 void bfs(){
20     int i;
21     point p,p1;
22     p1.x=n;
23     p1.y=n;
24     in[p1.x][p1.y]=1;
25     queue<point>q;
26     q.push(p1);
27     m[n][n]=t[n][n];
28     while(!q.empty()){
29         p=q.front();
30         q.pop();
31         in[p.x][p.y]=0;
32         for(i=0;i<4;i++){
33             p1.x=p.x+xx[i];
34             p1.y=p.y+yy[i];
35             if(p1.x>=1&&p1.x<=n&&p1.y>=1&&p1.y<=n&&(m[p1.x][p1.y]==-1||m[p1.x][p1.y]>m[p.x][p.y]+t[p1.x][p1.y])){
36                 m[p1.x][p1.y]=m[p.x][p.y]+t[p1.x][p1.y];
37                 if(!in[p1.x][p1.y]){
38                     q.push(p1);
39                     in[p1.x][p1.y]=1;
40                 }
41             }
42         }
43     }
44 }
45 
46 long long dfs(int x,int y){
47     if(x==n&&y==n)return 1;
48     if(dp[x][y])return dp[x][y];
49     int i;
50     for(i=0;i<4;i++){
51         int dx=x+xx[i],dy=y+yy[i];
52         if(dx>=1&&dx<=n&&dy>=1&&dy<=n&&m[dx][dy]<m[x][y]){
53             dp[x][y]+=dfs(dx,dy);
54         }
55     }
56     return dp[x][y];
57 }
58 
59 int main(){
60     while(scanf("%d",&n)!=EOF){
61         int i,j;
62         memset(m,-1,sizeof(m));
63         memset(dp,0,sizeof(dp));
64         memset(in,0,sizeof(in));
65         for(i=1;i<=n;i++){
66             for(j=1;j<=n;j++){
67                 scanf("%d",&t[i][j]);
68             }
69         }
70         bfs();
71         printf("%I64d
",dfs(1,1));
72     }
73     return 0;
74 }
View Code
原文地址:https://www.cnblogs.com/cenariusxz/p/4309559.html