题意
给定一幅图,每个点有一定权值,现在有一只老鼠在起始点(0,0),他能水平或者垂直移动1~k格之后,停在某点并获得权值,而且每次移动后所在的点,都要比刚离开的那个点的权值更大,求最多能获得多少权值。
思路
开始用bottom - up 的 dp做的,当前的最小是来自四个方向的最小,但出现的问题就是因为是按照从左到右,从上到下的顺序访问的,每个点可能更新的时候周围不是最优,解决的方法是按照值的大小顺序访问,就能保证正确。
很多人用的是记忆化搜索,也就是top-down(memoized)的dp,第一次发现这两个的区别,memoized是从源开始找到四周不会再更新的点,再返回向上。搜索的顺序直接就保证了最优。
记忆化搜索还是搜索,从一条路通到底那种,通许多次而已,而dp只是找每个i,j所代表的最大的权值,他的顺序是死的,只是从两个for循环按部就班的找,所以他的路径是死的,一开始这题跟hdu2571一样,那题只能是往有往下走,不能回头,而这题,是四个方向都可以走,是一个“搜索”,所以要用记忆化搜索。
记忆化搜索:这里的dp代表的是从后来所有点一路走来的最大权值,先不断递归,走到不能走了,就要返回,返回值是 dp[x][y]+a[x][y],然后回到上一层递归,继续for循环,再到走不动。。。。然后取max的最大值,这时候dp【x】【y】就已经是最优解了,以后遇到他直接返回就行;回溯完了dp【0】【0】就是从0,0走的最大值;
代码
bottom-up
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 105
#define INF 0x7f7f7f7f
using namespace std;
int n, k, cnt;
int val[ N ][ N ];
int dp[ N ][ N ];
struct Node {
int x, y;
int v;
bool operator< ( const Node n ) const { return v < n.v; }
} node[ N * N ];
bool path ( int a, int b, int x, int y ) {
if ( a < 0 || a >= n || b < 0 || b >= n )
return false;
if ( val[ a ][ b ] >= val[ x ][ y ] )
return false;
if ( !dp[ a ][ b ] )
return false;
return true;
}
int LIS () {
int mx = 0;
memset ( dp, 0, sizeof ( dp ) );
dp[ 0 ][ 0 ] = val[ 0 ][ 0 ];
// for ( int x = 0; x < n; ++x ) {
// for ( int y = 0; y < n; ++y ) {
for ( int p = 0; p < cnt; ++p ) {
int x = node[ p ].x;
int y = node[ p ].y;
// 走i步
for ( int i = 1; i <= k; ++i ) {
if ( path ( x - i, y, x, y ) )
dp[ x ][ y ] = max ( dp[ x ][ y ], dp[ x - i ][ y ] + val[ x ][ y ] );
if ( path ( x + i, y, x, y ) )
dp[ x ][ y ] = max ( dp[ x ][ y ], dp[ x + i ][ y ] + val[ x ][ y ] );
if ( path ( x, y - i, x, y ) )
dp[ x ][ y ] = max ( dp[ x ][ y ], dp[ x ][ y - i ] + val[ x ][ y ] );
if ( path ( x, y + i, x, y ) )
dp[ x ][ y ] = max ( dp[ x ][ y ], dp[ x ][ y + i ] + val[ x ][ y ] );
}
if ( mx < dp[ x ][ y ] )
mx = dp[ x ][ y ];
}
return mx;
}
int main () {
while ( ~scanf ( "%d%d", &n, &k ) && n != -1 && k != -1 ) {
cnt = 0;
for ( int i = 0; i < n; ++i )
for ( int j = 0; j < n; ++j ) {
scanf ( "%d", &val[ i ][ j ] );
node[ cnt ].x = i;
node[ cnt ].y = j;
node[ cnt++ ].v = val[ i ][ j ];
}
sort ( node, node + cnt );
int sol = LIS ();
printf ( "%d\n", sol );
}
return 0;
}
top-down
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 105
#define INF 0x7f7f7f7f
using namespace std;
int n, k;
int val[ N ][ N ];
int dp[ N ][ N ];
int dir[ 4 ][ 2 ] = {1, 0, 0, 1, -1, 0, 0, -1};
//下一个的坐标,当前的坐标
bool path ( int a, int b, int x, int y ) {
if ( a < n && b < n && a >= 0 && b >= 0 && val[ a ][ b ] > val[ x ][ y ] )
return true;
return false;
}
int dfs ( int x, int y ) {
if ( dp[ x ][ y ] )
return dp[ x ][ y ];
int ans = 0;
for ( int j = 1; j <= k; ++j )
for ( int i = 0; i < 4; ++i ) {
int xx = x + j * dir[ i ][ 0 ];
int yy = y + j * dir[ i ][ 1 ];
if ( path ( xx, yy, x, y ) )
ans = max ( ans, dfs ( xx, yy ) );
}
return dp[ x ][ y ] = ans + val[ x ][ y ];
}
int main () {
while ( ~scanf ( "%d%d", &n, &k ) ) {
if ( n == -1 && k == -1 )
break;
for ( int i = 0; i < n; i++ )
for ( int j = 0; j < n; j++ )
scanf ( "%d", &val[ i ][ j ] );
memset ( dp, 0, sizeof ( dp ) );
printf ( "%d\n", dfs ( 0, 0 ) );
}
return 0;
}