题目大意:
给你一个n*n的方格,每个格子上标有数字,你每次最多移动k个格子,并且只能一直沿着上下左右四个方向中的一个方向移动,问移动路线上所做停留点的数字之和最大是多少。
问题分析:
我们用dp+搜索的思路来解决这个问题,dp[x][y]表示从(x,y)点出发能够得到的最大和,那么我假设现在dp[x][y]的最大值已经知道,那么我只要走到(x,y)这里就不用再往后走了,因为最大值我已经知道了,就是dp[x][y]。
我们先考虑k = 1时的情况,如果是求(x,y)出发可以得到的最大值。
那么问题就变成了我需要知道(x,y)周围四个点(x,y - 1)、(x,y + 1)、(x - 1,y)、(x + 1,y)出发时候的最大值,进而我需要递归下去,最终直到某些不能走的点(这些点也就是整个格子中的最大值所在的点),然后递归回去就可以得到当前的最优解。
因此我们需要深度优先搜索dfs结合dp一起,我们用dp(x,y)表示寻求dp[x][y]的最优解,我们需要遍历四周的点(xx,yy);
可以得到状态转移方程:
dp[x][y] = max(dp[x][y],dp[xx][yy] + cheese[x][y]);
但是在一开始我们并不知道dp[xx][yy]的最优解,因此我们需要先递归求得dp[xx][yy]的解,那么状态转移方程就变成了:
dp[x][y] = max(dp[x][y],dfs(xx,yy)+ cheese[x][y]);
在每次搜索点(也就是dfs(x,y))的时候,我们先考察dp[x][y]是否已经存在最优解,如果存在我们直接返回最优解,以达到类似剪枝的目的(还是为了节约时间,去掉不必要的搜索次数);
代码:
#include <bits/stdc++.h>
using namespace std;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int maxn = 100 + 5;
bool vis[maxn][maxn];
int cheese[maxn][maxn], dp[maxn][maxn];
int n, k;
int dfs(int x, int y)
{
if(dp[x][y] != -1)
return dp[x][y];
dp[x][y] = cheese[x][y];
for(int i = 1; i <= k; i++)
{
for(int arr = 0; arr < 4; arr++)
{
int xx = x + dx[arr] * i;
int yy = y + dy[arr] * i;
if(xx < 0 || yy < 0 || xx >= n || yy >= n)
continue;
if(cheese[xx][yy] <= cheese[x][y])
continue;
dp[x][y] = max(dp[x][y], dfs(xx, yy) + cheese[x][y]);
}
}
return dp[x][y];
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> k)
{
if(n == -1 && k == -1)
break;
memset(dp, -1, sizeof(dp));
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
cin >> cheese[i][j];
cout << dfs(0, 0) << endl;
}
return 0;
}