矩阵取数游戏

本文介绍了一种矩阵取数游戏的算法实现,旨在通过特定的游戏规则寻找最优解,即在给定的矩阵中按特定规则取数以获得最大得分。

题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
4.游戏结束总得分为m次取数得分之和。
想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式:
输入文件game.in包括n+1行:
第1行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。
数据范围:
60%的数据满足:1<=n, m<=30,答案不超过10^16
100%的数据满足:1<=n, m<=80,0<=aij<=1000

根本不想写高精度。。首先,状态我们怎么定义。取i次好像不太好写。前i个 以i结尾好像都不太好写。
我们想,取数的时候要么取走最右边的,要么取走最左边的,所以状态或许跟左右有关?
f[i][j]表示最左边是第i个数,最右边是第j个数的状态得分最大值。然后瞎推一下就好了。懒得写高精度去复制了int128模板,但是NOIP不让用0.0。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e2+2;

__int128 ma[MAXN][MAXN];
__int128 dp[MAXN][MAXN][MAXN],ans,sum=0;
__int128 poww[MAXN];
//dp[i][j][l]=max(dp[i-1][j]+(1<<k)*w[i-1],dp[i][j+1]+(1<<k)*w[j+1]);

//dp[][]

void Print(__int128 x)
{
    if(x==0) return;
    else if(x) Print(x/10);
    putchar(x%10+'0');
}

int n,m;
int main(){
    memset(dp,0,sizeof(dp));
    memset(ma,0,sizeof(ma));
    memset(poww,0,sizeof(poww));
    scanf("%d%d",&n,&m);
    poww[0]=1;
    for(int i=1;i<=m;i++)poww[i]=poww[i-1]*2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&ma[i][j]);
        }
    for(int l=1;l<=n;l++)
        for(int i=1;i<=m;i++)
            for(int j=m;j>=i;j--){
                dp[i][j][l]=max(dp[i-1][j][l]+poww[m-j+i-1]*ma[l][i-1],dp[i][j+1][l]+poww[m-j+i-1]*ma[l][j+1]);
            }
    for(int l=1;l<=n;l++){
        ans=-1;
        for(int i=1;i<=m;i++){
            ans=max(ans,dp[i][i][l]+poww[m]*ma[l][i]);//+(1<<(m-1))*ma[l][i]
        }
        sum+=ans;
    }
    if(ans==0) printf("0");
    else Print(sum);
    return 0;
}
### NOI 2013 矩阵游戏 解题报告 #### 背景描述 题目涉及两个玩家轮流在一个 \(s \times n\) 的矩阵中选元素,目标是在若干轮操作之后使得最终得分最大。每个位置上的值会随着被选择次增加而翻倍。 #### 动态规划建模 为了有效解决这个问题,可以构建动态规划模型来追踪当前状态下可能获得的最大分。定义状态转移方程如下: \[ dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + matrix[i][j]*2^{(i+j)} \] 这里 `dp[i][j]` 表示到达第 i 行 j 列时能够得到的最大总分;\(matrix[i][j]\) 是原始输入矩阵中的值;指部分代表该格子被访问过的次[^5]。 #### 循环节优化 考虑到直接计算上述 DP 方案的时间复杂度较高,在实际实现过程中可以通过观察到的循环特性来进行加速。具体来说,如果存在某个时刻某项满足条件 \(num \% k == 1\) ,则意味着这一序列已经形成了完整的周期模式[^3]。利用这一点可以在一定程度上减少不必要的重复运算。 #### 实现细节 基于以上分析,下面给出 Python 版本的核心算法框架: ```python MOD = 1_000_000_007 def solve(matrix, rows, cols): # 初始化DP表 dp = [[0]*(cols+1) for _ in range(rows+1)] # 计算每一层的结果并更新至DP表内 for r in range(1, rows + 1): for c in range(1, cols + 1): power = pow(2, (r+c)-2, MOD) value = matrix[r-1][c-1] dp[r][c] = ((max(dp[r-1][c], dp[r][c-1])) % MOD + (value * power)) % MOD return dp[-1][-1] # 示例调用 if __name__ == "__main__": from sys import stdin input_data = list(map(int, stdin.read().strip().split())) row_count, col_count = input_data[:2] values = input_data[2:] game_matrix = [] index = 0 for _ in range(row_count): temp_row = [values[index+i] for i in range(col_count)] game_matrix.append(temp_row) index += col_count result = solve(game_matrix, row_count, col_count) print(result) ``` 此代码片段实现了基本的动态规划求解过程,并考虑到了大整溢出的情况通过模运算加以规避。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值