第三十四章:格子取数问题

格子取数问题

    题目详情:有n*n个格子,每个格子里有正数或者0,从最左上角往最右下角走,只能向下和向右,一共走两次(即从左上角走到右下角走两趟),把所有经过的格子的数加起来,求最大值SUM,且两次如果经过同一个格子,则最后总和SUM中该格子的计数只加一次。

动态规划:

DP[step,i,j]表示的是第step步(每两步算一步)走到第i行和第j行(不需要知道列,因为走了step步,肯定走到的是step这个数,如下图所示)

例如:DP[5,1,2],DP[5,1,3],DP[5,2,2],DP[5,2,3],用加粗表示位置DP[5,1,2]    DP[5,1,3]    DP[5,2,2]    DP[5,2,3] (加红表示要达到的状态DP[6,2,3]
0 1 2 3 4    0 1 2 3 4    0 1 2 3 4    0 1 2 3 4
1 2 3 4 5    1 2 3 4 5    1 2 3 4 5    1 2 3 4 5
2 3 4 6    2 3 4 5 6    2 3 4 6    2 3 4 6
3 4 5 6 7    3 4 6 7    3 4 5 6 7    3 4 6 7
4 5 6 7 8    4 5 6 7 8    4 5 6 7 8    4 5 6 7 8
  因此:

DP[6,2,3] = Max(DP[5,1,2] ,DP[5,1,3],DP[5,2,2],DP[5,2,3]) + 6,2和6,3格子中对应的数值    (式一) 

  上面(式一)所示的这个递推没有涉及:“如果两次经过同一个格子,那么该数只加一次的这个条件”,讨论这个条件需要换一个例子,以DP[6,2,2]为例:DP[6,2,2]可以由DP[5,1,1],DP[5,1,2],DP[5,2,2]到达,但由于i = j,也就是2次走到同一个格子,那么数值只能加1次。
  所以当i = j时,

DP[6,2,2] = Max(DP[5,1,1],DP[5,1,2],DP[5,2,2]) + 6,2格子中对应的数值                         (式二

#include <iostream>     
#include <algorithm>
#include <functional>
using namespace std;    

const int N = 202;  
const int inf = 1000000000;  //无穷大  
int dp[N * 2][N][N];    
bool isValid(int step,int x1,int x2,int n) { //判断状态是否合法  
	int y1 = step - x1, y2 = step - x2;  
    return ((x1 >= 0) && (x1 < n) && (x2 >= 0) && (x2 < n) && (y1 >= 0) && (y1 < n) && (y2 >= 0) && (y2 < n));  
}  
  
int getValue(int step, int x1,int x2,int n) {  //处理越界 不存在的位置 给负无穷的值  
    return isValid(step, x1, x2, n)?dp[step][x1][x2]:(-inf);  
}  
  
//状态表示dp[step][i][j] 并且i <= j, 第step步  两个人分别在第i行和第j行的最大得分 时间复杂度O(n^3) 空间复杂度O(n^3)   
int getAnswer(int a[N][N],int n) {  
	int P = n * 2 - 2; //最终的步数  
	int i,j,step;  
      
    //不能到达的位置 设置为负无穷大  
    for (i = 0; i < n; ++i) {  
        for (j = i; j < n; ++j) {  
            dp[0][i][j] = -inf;  
        }  
      
    }  
    dp[0][0][0] = a[0][0];  
  
      for (step = 1; step <= P; ++step) {  
        for (i = 0; i < n; ++i) {  
            for (j = i; j < n; ++j) {  
                dp[step][i][j] = -inf;  
                if (!isValid(step, i, j, n)) { //非法位置  
                    continue;  
                }  
                //对于合法的位置进行dp  
                if (i != j) {  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i - 1, j - 1, n));  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i - 1, j, n));  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i, j - 1, n));  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i, j,n));  
                    dp[step][i][j] += a[i][step - i] + a[j][step - j];  //不在同一个格子,加两个数  
                }  
                else {  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i - 1, j - 1, n));  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i - 1, j,  n));  
                    dp[step][i][j] = max(dp[step][i][j], getValue(step - 1, i, j,  n));  
                    dp[step][i][j] += a[i][step - i]; // 在同一个格子里,只能加一次  
                }  
                  
            }  
        }  
    }  
    return dp[P][n - 1][n- 1];  
}  

int main()
{
    int map[N][N]={    
    {2,0,8,0,2},    
    {0,0,0,0,0},    
    {0,3,2,0,0},    
    {0,0,0,0,0},    
    {2,0,8,0,2}};   
	cout<<getAnswer(map,5);
    return 0;
}   




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
格子游戏是一种使用格子面板的游戏,每个格子可以是活细胞(1)或死细胞(0)。每个细胞与其八个相邻位置的细胞遵循四条生存定律来决定其下一状态。根据给定的规则,格子游戏会根据当前细胞的状态和周围细胞的状态来更新每个细胞的状态,最终根据记分标准来决定胜负。 根据给定的样例输入和输出,我们可以使用位运算符来解决这个问题。可以使用原地算法来更新面板上所有的格子,但需要注意处理边界问题。当活细胞侵占了面板边界时,需要特别处理。 具体的解决方法如下: 1. 遍历面板上的每个格子,计算其周围活细胞的量。 2. 根据当前格子的状态和周围活细胞的量,按照规则更新格子的状态。 3. 遍历面板上的每个格子,将格子的状态右移一位来更新格子的状态。 通过以上方法,我们可以实现格子游戏的更新操作。 请注意,以上解答仅针对给定的问题描述和代码片段,具体实现可能需要根据实际情况进行调整。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Scratch益智游戏作品:棋类游戏 挤格子](https://download.csdn.net/download/leyang0910/87641408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [LintCode 1301. 生命游戏 JavaScript算法](https://blog.csdn.net/SmallTeddy/article/details/108635537)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值