NOIP2007提高组第二轮T3:矩阵取数游戏

题目链接

[NOIP2007 提高组] 矩阵取数游戏

题目描述

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n × m n \times m n×m 的矩阵,矩阵中的每个元素 a i , j a_{i,j} ai,j 均为非负整数。游戏规则如下:

  1. 每次取数时须从每行各取走一个元素,共 n n n 个。经过 m m m 次后取完矩阵内所有元素;
  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
  3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 × 2 i \times 2^i ×2i,其中 i i i 表示第 i i i 次取数(从 1 1 1 开始编号);
  4. 游戏结束总得分为 m m m 次取数得分之和。

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

输入格式

输入文件包括 n + 1 n+1 n+1 行:

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

2 ∼ n + 1 2\sim n+1 2n+1 行为 n × m n \times m n×m 矩阵,其中每行有 m m m 个用单个空格隔开的非负整数。

输出格式

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

样例 #1

样例输入 #1

2 3
1 2 3
3 4 2

样例输出 #1

82

提示

【数据范围】

对于 60 % 60\% 60% 的数据,满足 1 ≤ n , m ≤ 30 1\le n,m\le 30 1n,m30,答案不超过 1 0 16 10^{16} 1016
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 80 1\le n,m\le 80 1n,m80 0 ≤ a i , j ≤ 1000 0\le a_{i,j}\le1000 0ai,j1000

算法思想

根据题目描述,每次都要从各行的行首或行尾取走一个元素,一共取 m m m次,求出取数后的最大得分。不难发现,在取数的过程中,行与行之间相互独立,因此可以对每一行单独计算取数的最大得分。

根据得分的计算规则,每行取数的得分 = 被取走的元素值 × 2 i \times 2^i ×2i,其中 i i i 表示第 i i i 次取数(从 1 1 1 开始编号),而每次取数有两种情况,行首或行尾取走一个元素,如下图所示:

在这里插入图片描述
那么,每行取数的得分之和的最大值除了与被取走的元素值 × 2 i \times 2^i ×2i有关,还与剩余区间的得分相关,可以使用区间型动态规划来处理。

状态表示

f [ i ] [ j ] f[i][j] f[i][j]表示在一行中区间 [ i , j ] [i,j] [i,j]取数的最大得分。

状态计算

根据取数规则,只能取走行首或行尾元素,因此要计算当前状态 f [ i ] [ j ] f[i][j] f[i][j],可以根据取数的位置分成两种情况:

  • 取走行首元素,也就是 i i i位置上的元素,得分为 f [ i + 1 ] [ j ] + w [ i ] × 2 m − j + i f[i+1][j] + w[i]\times2^{m-j+i} f[i+1][j]+w[i]×2mj+i
  • 取走行尾元素,也就是 j j j位置上的元素,得分为 f [ i ] [ j − 1 ] + w [ j ] × 2 m − j + i f[i][j-1] + w[j]\times2^{m-j+i} f[i][j1]+w[j]×2mj+i

其中:

  • f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j] f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]表示剩余区间的得分最大值;
  • w 表示取数位置上元素的分值。对区间 表示取数位置上元素的分值。对区间 表示取数位置上元素的分值。对区间[i,j] 取数时,意味着之前已经取走了 取数时,意味着之前已经取走了 取数时,意味着之前已经取走了m-(j-i+1) 个数,当前是第 个数,当前是第 个数,当前是第m-j+i 次取数,因此应加上 次取数,因此应加上 次取数,因此应加上w\times2^{m-j+i}$

初始状态

注意,当区间长度为 1 1 1时, f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j] f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]表示的区间为空,此时状态应为 0 0 0

时间复杂度

状态数为 m × m m\times m m×m,状态计算的时间复杂度为 O ( 1 ) O(1) O(1),一共要计算 n n n行,因此时间复杂度为 O ( n × m 2 ) = 8 0 3 = 512000 O(n\times m^2)=80^3=512000 O(n×m2)=803=512000

代码实现(60分)

对于 60 % 60\% 60% 的数据,满足 1 ≤ n , m ≤ 30 1\le n,m\le 30 1n,m30,答案不超过 1 0 16 10^{16} 1016

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100;
int n, m;
LL w[N], f[N][N];
LL work()
{//枚举区间长度for(int len = 1; len <= m; len ++)//枚举开始位置for(int i = 1; i + len - 1 <= m; i ++){int j = i + len - 1; //结束位置int t = m - j + i; //第t次取数f[i][j] = max(f[i + 1][j] + w[i] * (1 << t), f[i][j - 1] + w[j] * (1 << t));}return f[1][m];
}
int main()
{cin >> n >> m;LL res = 0;for(int i = 1; i <= n; i ++){for(int j = 1; j <= m; j ++)cin >> w[j];res += work();}cout << res << endl;return 0;
}

代码实现(100分)

对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 80 1\le n,m\le 80 1n,m80 0 ≤ a i , j ≤ 1000 0\le a_{i,j}\le1000 0ai,j1000

高精度实现

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef vector<int> VI;
typedef long long LL;
const int N = 90, D = 1e8; //表示大整数每个部分的位数
int n, m;
int w[N];
VI f[N][N];
VI power2[N];VI add(VI a, VI b)
{static VI c;c.clear();for (int i = 0, t = 0; i < a.size() || i < b.size() || t; i ++ ){if (i < a.size()) t += a[i];if (i < b.size()) t += b[i];c.push_back(t % D); //压位t /= D; //压位}return c;
}VI mul(VI a, int b)
{static VI c;c.clear();LL t = 0;for (int i = 0; i < a.size() || t; i ++ ){if (i < a.size()) t += (LL)a[i] * b;c.push_back(t % D);t /= D;}return c;
}VI Max(VI A, VI B)
{if (A.size() != B.size()){if (A.size() > B.size()) return A;return B;}for (int i = A.size() - 1; i >= 0; i -- ){if (A[i] > B[i]) return A;if (A[i] < B[i]) return B;}return A;
}void print(VI a)
{printf("%d", a.back());//压位处理,中间位数不足8位则补0for (int i = a.size() - 2; i >= 0; i -- ) printf("%08d", a[i]); puts("");
}VI work()
{for (int len = 1; len <= m; len ++ )for (int i = 1; i + len - 1 <= m; i ++ ){int j = i + len - 1;int t = m - j + i;//区间长度为1时if (i == j) f[i][j] = mul(power2[t], w[j]);else{auto left = add(mul(power2[t], w[i]), f[i + 1][j]);auto right = add(mul(power2[t], w[j]), f[i][j - 1]);f[i][j] = Max(left, right);}}return f[1][m];
}int main()
{cin >> n >> m;//求2的次方power2[0] = {1};for (int i = 1; i <= m; i ++ ) power2[i] = mul(power2[i - 1], 2);//将res初始化为0VI res(1, 0);for (int i = 1; i <= n; i ++ ){for (int j = 1; j <= m; j ++ ) cin >> w[j];res = add(res, work());}print(res);return 0;
}

__int128实现

由于 m ≤ 80 m\le 80 m80 0 ≤ a i , j ≤ 1000 0\le a_{i,j}\le1000 0ai,j1000,那么每行的最大值为 80 × 1000 × 2 80 80\times1000\times2^{80} 80×1000×280,不会超过 2 100 2^{100} 2100,因此可以使用__int128来实现。

注意:

  • __int128并不在c++标准中,它存在于GCC编译器,且仅GCC4.6 及以上的64位版本支持。所以在使用时,要确认OJ是否支持。
  • cout不能直接输出__int128
#include <iostream>
using namespace std;
typedef __int128 INT;
const int N = 100;
int n, m;
int w[N];
INT f[N][N];
INT work()
{//枚举区间长度for(int len = 1; len <= m; len ++)//枚举开始位置for(int i = 1; i + len - 1 <= m; i ++){int j = i + len - 1; //结束位置int t = m - j + i; //第t次取数INT p = 1; p = p << t;f[i][j] = max(f[i + 1][j] + w[i] * p, f[i][j - 1] + w[j] * p);}return f[1][m];
}
void print(INT x)
{if(x > 9) print(x / 10);cout << x % 10;
}
int main()
{cin >> n >> m;INT res = 0;for(int i = 1; i <= n; i ++){for(int j = 1; j <= m; j ++)cin >> w[j];res += work();}//注意,cout不能直接输出__int128print(res);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/236748.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记7——数据库基础知识以及mysql的查询语句

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/contents/3/199913.html 数据库 三个概念区分 DB&#xff1a;数据库&#xff0c;存储数据的仓库&#xff0c;有组织的数据容器DBMS:数据库管理系统SQL&#xff1a;几乎所有的DBMS都支持…

人工智能_机器学习056_拉格朗日乘子法原理推导_公式由来详解_原理详解---人工智能工作笔记0096

https://blog.csdn.net/Soft_Po/article/details/118332454 这里有老师的一篇文章介绍拉格朗日乘子法的原理推导 结合老师的这篇文章我们来看一下详细的推导过程 可以看到上一节我们说,一个有条件的,函数,可以转换为一个,无条件的函数, 根据拉格朗日乘子法,可以创建出一个等…

python使用记录

1、VSCode添加多个python解释器 只需要将对应的python.exe的目录&#xff0c;添加到系统环境变量中即可&#xff0c;VSCode会自动识别及添加 2、pip 使用 pip常用命令和一些坑 查看已安装库的版本号 pip show 库名称 通过git 仓库安装第三方库 pip install git仓库地址

Linux系统常用指令

1.使用xshell登录到云服务器的Linux系统&#xff1a; ssh 用户名公网IP&#xff0c;例如&#xff1a; ssh root111.11.111. 2.添加用户 adduser 用户名&#xff0c;例如&#xff1a; adduser user 3.为用户设置密码 passwd 用户名&#xff0c;例如&#xff1a; passwd …

Cloudflare Email Routing 免费邮件发送服务

Cloudflare Email Routing 免费邮件发送(作为 Service 服务)用于 Workers/Pages 项目中。 原文链接: https://willin.wang/blog/cloudflare-send-email-service 准备工作 准备一个域名,例如 example.com。现在,在 cloudflare-dashboard 中添加一个网站并构建您的域名。这…

【JavaScript】3.4 JavaScript在现代前端开发中的应用

文章目录 1. 用户交互2. 动态内容3. 前端路由4. API 请求总结 JavaScript 是现代前端开发的核心。无论是交互效果&#xff0c;还是复杂的前端应用&#xff0c;JavaScript 都发挥着关键作用。在本章节中&#xff0c;我们将探讨 JavaScript 在现代前端开发中的应用&#xff0c;包…

uniapp小程序分包页面引入wxcomponents(vue.config.js、copy-webpack-plugin)

实例&#xff1a;小程序添加一个源生小程序插件&#xff0c;按照uniapp官方的说明&#xff0c;要放在wxcomponents。后来发现小程序超2m上传不了。 正常的编译情况 会被编译到主包下 思路&#xff1a;把wxcomponents给编译到分包sub_package下 用uniapp的vue.config.js自定义…

机器学习的复习笔记3-回归的细谈

一、回归的细分 机器学习中的回归问题是一种用于预测连续型输出变量的任务。回归问题的类型和特点如下&#xff1a; 线性回归&#xff08;Linear Regression&#xff09;&#xff1a;线性回归是回归问题中最简单的一种方法。它假设自变量与因变量之间存在线性关系&#xff0c…

高校智慧用电管理平台

高校智慧用电管理平台是一种基于物联网、云计算、大数据等技术的智能化用电管理系统&#xff0c;旨在实现高校用电的实时监测、智能控制、数据分析和管理决策。 具体来说&#xff0c;该平台通常包括以下功能和特点&#xff1a; 实时监测&#xff1a;通过安装传感器、智能终端等…

基于SpringBoot的图书推荐系统的

摘 要 网络信息技术的高速发展&#xff0c;使得高校图书馆的服务空间日益扩大&#xff0c;依据个人特点的针对性服务逐渐成为新服务模式的主导趋势。对于大多数用户而言&#xff0c;很难在大量的学术图书馆中快速找到他们想要的材料。另外&#xff0c;随着时代的不断发展&…

class-dump 混淆加固、保护与优化原理

​ 进行逆向时&#xff0c;经常需要dump可执行文件的头文件&#xff0c;用以确定类信息和方法信息&#xff0c;为hook相关方法提供更加详细的数据.class-dump的主要用于检查存储在Mach O文件的Objective-C中的运行时信息&#xff0c;为类&#xff0c;类别和协议生成声明信息&am…

做外贸价格差也有等级

最近无意中看到一句话&#xff0c;挺有感触的&#xff0c;分享给大家&#xff0c;或许我们在谈客户的时候可以用到或者是作为评判的标准来对客户进行一定的定位。 差价2%&#xff0c;可能因为服务 差价5%&#xff0c;应该因为工艺 差价10%&#xff0c;肯定是因为材料 差价2…