[动态规划]矩阵取数游戏

矩阵取数游戏

题目描述

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n行*m列的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:

  1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有的元素;

  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;

  3. 每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分 = 被取走的元素值*i,其中i表示第i次取数(从1开始编号);

  4. 游戏结束总得分为m次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

关于输入

包括n+1行;

第一行为两个用空格隔开的整数n和m。

第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开

l<=n,m<=80,1<=aij<=1000

关于输出

仅包含1行,为一个整数,即输入矩阵取数后的最大的分。

例子输入
复制代码
2 3
1 2 3
3 4 2
例子输出
复制代码
34
解题分析

这个问题的具体描述是:给定一个n行m列的矩阵,每次从每行中取走一个元素(只能是行首或行尾的元素),每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*i,其中i表示第i次取数。求取数后的最大得分。

程序的主要思路如下:

  1. 首先,程序读取两个整数n和m,然后读取n行m列的矩阵,存储在数组`a`中。

  2. 然后,程序对每一行进行处理。对于每一行,程序初始化一个二维数组`dp`,`dp[i][j]`表示从第i个元素到第j个元素(包括i和j)的子数组中,取数的最大得分。

  3. 程序遍历所有可能的子数组长度(从1到m)。对于每一个子数组长度,程序遍历所有可能的子数组的起始位置。

  • 如果子数组长度为1,那么`dp[i][j]`就等于`m * a[row][i]`,因为子数组只有一个元素,所以取数的得分就是`m * a[row][i]`。

  • 如果子数组长度大于1,那么`dp[i][j]`就等于`dp[i+1][j] + (m-len+1) * a[row][i]`和`dp[i][j-1] + (m-len+1) * a[row][j]`中的较大值。这是因为,我们可以选择取第i个元素或者第j个元素,所以我们需要比较这两种选择的得分,取较大的那个。

  1. 对于每一行,`dp[0][m-1]`就是这一行的最大得分。程序将所有行的最大得分累加起来,就得到了总的最大得分。

这个程序的时间复杂度是O(n*m^2),因为它需要对每一行遍历所有可能的子数组。如果矩阵的大小非常大,那么这个程序可能会运行得比较慢。

代码实现
cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

int dp[85][85];
int a[85][85];


int main() {
    int n,m; cin>>n>>m;
    int ans=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            cin>>a[i][j];
        }
    for(int row=0;row<n;row++){
        memset(dp,0,sizeof(dp));
        for(int len=1;len<=m;len++)
            for(int i=0;i<m && i+len-1<m;i++){
                int j=i+len-1;
                if(len==1){
                    dp[i][j]=m*a[row][i];
                }
                else{
                    dp[i][j]=max(dp[i+1][j]+(m-len+1)*a[row][i],dp[i][j-1]+(m-len+1)*a[row][j]);
                }
            }
        ans+=dp[0][m-1];
    }
    cout<<ans<<endl;
	return 0;
}
当然,使用记忆搜索法也不错

这个程序是用来解决"矩阵取数游戏"问题的改进版本。它仍然使用了动态规划的思想,但是采用了记忆化搜索的方法,将递归的过程中重复的子问题的解存储起来,避免了重复计算,从而提高了效率。

程序的主要思路如下:

  1. 首先,程序读取两个整数n和m,然后读取n行m列的矩阵,存储在数组`a`中。

  2. 然后,程序对每一行进行处理。对于每一行,程序使用一个记忆化搜索的函数`f(i, j)`来计算从第i个元素到第j个元素(包括i和j)的子数组中,取数的最大得分。

  3. 函数`f(i, j)`的定义如下:

  • 如果`dp[i][j]`已经计算过,那么直接返回`dp[i][j]`的值。

  • 如果`i`等于`j`,那么`dp[i][j]`就等于`m * a[row][i]`,因为子数组只有一个元素,所以取数的得分就是`m * a[row][i]`。

  • 如果`i`不等于`j`,那么`dp[i][j]`就等于`f(i+1, j) + (m-j+i) * a[row][i]`和`f(i, j-1) + (m-j+i) * a[row][j]`中的较大值。这是因为,我们可以选择取第i个元素或者第j个元素,所以我们需要比较这两种选择的得分,取较大的那个。

  1. 对于每一行,`f(0, m-1)`就是这一行的最大得分。程序将所有行的最大得分累加起来,就得到了总的最大得分。

这个程序的时间复杂度是O(n*m^2),因为它需要对每一行遍历所有可能的子数组。如果矩阵的大小非常大,那么这个程序可能会运行得比较慢。

注意,这个程序假设输入的矩阵的行数和列数不超过80,如果实际问题中矩阵的行数或列数可能超过这个值,那么需要相应地调整数组的大小。

代码实现2
cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

int dp[85][85];
int a[85][85];
int n,m,row;

int f(int i,int j){
    if(dp[i][j]){
        return dp[i][j];
    }
    if(i==j){
        return dp[i][j]=a[row][i]*m;
    }
    else{
        return dp[i][j]=max(f(i+1,j)+(m-j+i)*a[row][i],f(i,j-1)+(m-j+i)*a[row][j]);
    }
}

int main() {
    cin>>n>>m;
    int ans=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            cin>>a[i][j];
        }
    for(row=0;row<n;row++){
        memset(dp,0,sizeof(dp));
        ans+=f(0,m-1);
    }
    cout<<ans<<endl;
	return 0;
}
相关推荐
今夕资源网6 小时前
.sav游戏存档在线编辑器 .sav文件存档转json文件 html源码
游戏·游戏存档编辑器·sav游戏存档在线编辑器·游戏存档在线编辑器·sav游戏存档在线编辑·sav游戏存档编辑·游戏存档修改
byzh_rc6 小时前
[算法设计与分析-从入门到入土] 动态规划
算法·动态规划
IT猿手8 小时前
三维动态避障路径规划:基于部落竞争与成员合作算法(CTCM)融合动态窗口法DWA的无人机三维动态避障方法研究,MATLAB代码
算法·matlab·动态规划·无人机·路径规划·动态路径规划
我无止境9 小时前
和平精英模拟器进入对局卡成PPT怎么办?(可能解决黑屏问题)
游戏
RedMery10 小时前
厄米特矩阵的性质
线性代数·矩阵
烧冻鸡翅QAQ12 小时前
从0开始的游戏编程——Cocos Creator开发
游戏
nju_spy14 小时前
12月力扣每日一题(划分dp + 单调栈 + 堆 + 会议安排)
算法·leetcode·二分查找·动态规划·滑动窗口·单调栈·最大堆
熊猫钓鱼>_>15 小时前
解决Web游戏Canvas内容在服务器部署时的显示问题
服务器·前端·游戏·canvas·cors·静态部署·资源路径
喵了几个咪16 小时前
Go单协程事件调度器:游戏后端的无锁有序与响应时间掌控
开发语言·游戏·golang
2401_8414956416 小时前
【LeetCode刷题】零钱兑换
数据结构·python·算法·leetcode·动态规划·数组·时间复杂度