Maja(DP)

题目描述

Maja和蜜蜂在一片神奇的草地上为花授粉,这块草地可以表示为一个n行m列的矩形,在第i行第j列中有CIJ朵没有授粉的花。

Maja的蜂巢位于第a行第b列,她将从她的蜂巢开始为这些花授粉,去草地上的某些块授粉,然后再返回她的蜂巢。每次操作,Maja可以向相邻的上下左右中的一个方格移动,而且她永远不会离开草地。每次她经过的某块草地,都会给这块草地上所有未授粉的花授粉。但草地很神奇,一旦Maja离开草地(i,j),所有授粉的花都会消失,新的未授粉的花又会在这片土地上生长。

由于Maja不可能永远飞行,她会在飞过k个格子后感到疲倦,并乐意向她的蜜蜂朋友们讲述她的冒险故事。请问,在Maja授粉并在k步后返回蜂巢,她能授粉的花的数量是多少?

输入格式

第一行包含正整数n,m(2≤n,m≤100),a(1≤a≤n),b(1≤b≤m)和k(2≤k≤1000000000),题目保证k是偶数

接下来n行,每行输入m个数字,表示第i行第j列有未授粉的花Cij(0≤Cij≤1000000000)朵。

题目保证在蜂巢的位置不会有任何花。

输出格式

输出一个数,表示Maja最多能授粉的花的数量。

样例

样例输入1

2 2 1 1 2
0 1
2 10

样例输出1

2

样例输入2

2 2 1 1 4
0 5
5 10

样例输出2

20

样例输入3

3 3 2 2 6
5 1 0
1 0 3
1 3 3

样例输出3

15

题解:

考场的时候竟然连dp都没想到,主要是因为k太大,然后就打了搜索,自己连时间复杂度都不算

然后就爆了

如果我们算到了走k/2的最大值那么原路走明显是对于k最大的

定义dp[r][i][j]表示从起点到(i,j)用了r次的最大值

然后转化就是很简单,从四个点转化即可

最后用滚动数组优化呀

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>
using namespace std;
#define ll long long
const int MAXN = 103;
ll n , m , a , b ;
ll k;
ll c[MAXN][MAXN];
ll dp[2][MAXN][MAXN];
int dis[4][2] = { { 1, 0  } , { -1 , 0 } ,{ 0 , 1 } , { 0 , -1 } };
int main(){
    scanf( "%lld%lld%lld%lld%lld" , &n , &m , &a , &b , &k );
    memset( dp , -0x7f , sizeof( dp ) );
    dp[0][a][b] = 0;
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j <= m ; j ++ )
            scanf( "%lld" ,&c[i][j] );
    k /= 2;
    int r;
    for( r = 1 ; r <= min( k , m * n ); r ++ ){
        int now = r % 2;
        int last = now == 1 ? 0 : 1;
        for( int i = 1 ; i <= n ; i ++ ){
            for( int j = 1 ; j <= m ; j ++ ){
                for( int l = 0 ;l < 4 ; l ++ ){
                    int tx = i + dis[l][0];
                    int ty = j  + dis[l][1];
                    if( tx > 0 && ty > 0 && tx <= n && ty <= m && dp[last][tx][ty] >= 0 )
                        dp[now][i][j] = max( dp[now][i][j] , dp[last][tx][ty] + c[i][j] );
                }
            }
        }
    }
    r --;
    r %= 2;
    ll ans = 0;
    for( int i = 1 ; i <= n ; i ++ ){
        for( int j = 1 ; j <= m ; j ++ ){
            if( dp[r][i][j] < 0 ) continue;
            ans = max( ans , dp[r][i][j] * 2 - c[i][j] );
        }
    }
    printf( "%lld" , ans );
}

 然后就A了???呵呵

其实是错的

如果k>n*m怎么办?

那么就要改变一下

对于一个已经走到一个点使dp[r][i][j]最大

那么我们没必要再往回走只需要找一个相邻点最大权值的点来回走,这样最后直接走到起点即可

首先把k/=2

然后先算一个来回的加上来回走两个点的即可

就是dp[r][i][j]*2-c[i][[j] + (k-r)*(c[i][j]+max(c[i-1][j],c[i][j-1],c[i+1][j],c[i][j+1])

k是已经被/2的

走两个点一个来回需要两步,也就是不停来回走的次数只有(k-r)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>
using namespace std;
#define ll long long
const int MAXN = 103;
ll n , m , a , b ;
ll k;
ll c[MAXN][MAXN];
ll dp[2][MAXN][MAXN];
int dis[4][2] = { { 1, 0  } , { -1 , 0 } ,{ 0 , 1 } , { 0 , -1 } };
int main(){
    scanf( "%lld%lld%lld%lld%lld" , &n , &m , &a , &b , &k );
    memset( dp , -0x7f , sizeof( dp ) );
    dp[0][a][b] = 0;
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j <= m ; j ++ )
            scanf( "%lld" ,&c[i][j] );
    int r;
    k /= 2;
    ll ans = 0 ;
    for( r = 1 ; r <= min( k, m * n ); r ++ ){
        int now = r % 2;
        int last = now == 1 ? 0 : 1;
        for( int i = 1 ; i <= n ; i ++ ){
            for( int j = 1 ; j <= m ; j ++ ){
                for( int l = 0 ;l < 4 ; l ++ ){
                    int tx = i + dis[l][0];
                    int ty = j  + dis[l][1];
                    if( tx > 0 && ty > 0 && tx <= n && ty <= m && dp[last][tx][ty] >= 0 )
                        dp[now][i][j] = max( dp[now][i][j] , dp[last][tx][ty] + c[i][j] );
                }
                if( dp[now][i][j] <= 0 ) continue;
                ll tmp = dp[now][i][j] * 2 - c[i][j] + ( k - r ) * ( c[i][j] + max( c[i][j-1] , max( c[i-1][j] , max( c[i+1][j] , c[i][j+1] ) ) ) );
                ans = max( tmp , ans );
            }
        }
    }
    printf( "%lld" , ans );
}
发布了68 篇原创文章 · 获赞 7 · 访问量 3844

猜你喜欢

转载自blog.csdn.net/weixin_43823476/article/details/99992215
DP
DP?