[DP/记忆化搜索] HDU 1078

题意

给定一幅图,每个点有一定权值,现在有一只老鼠在起始点(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 
#include 
#include 
#include 

#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 
#include 
#include 
#include 

#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;
}

你可能感兴趣的:(ACM,12-动态规划)