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位则补0
    for (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初始化为0
    VI 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不能直接输出__int128
    print(res);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

销售心理学 如何了解客户的购买心理激发客户购买兴趣

销售心理学 如何了解客户的购买心理激发客户购买兴趣 在销售的世界里&#xff0c;掌握客户的购买心理&#xff0c;如同一把神奇的钥匙&#xff0c;能够解锁客户内心的需求和兴趣。如何巧妙地运用销售心理学&#xff0c;激发客户的购买欲望呢&#xff1f;以下是一些建议&#x…

实战|信息泄露

0x01系统初探 通过fofa对大学进行搜索 fofa:host"edu.cn" &amp;&amp; status_code"200"在随意的翻阅查看时&#xff0c;发现访问xxx.edu.cn登录页面会优先访问登录后的页面&#xff0c;再跳转至登录页面。盲猜应该是前端校验&#xff0c;可以通过…

DEM分析

一、实验名称&#xff1a; DEM分析 二、实验目的&#xff1a; 通过本实验练习&#xff0c;掌握DEM的建立与应用基本方法。 三、实验内容和要求&#xff1a; 实验内容&#xff1a; 利用ARCGIS软件相关分析工具及实验数据&#xff0c;创建DEM&#xff0c;并计算相应坡度的区…

FlashDuty Changelog 2023-10-30 | 告警路由与 Slack 应用

FlashDuty&#xff1a;一站式告警响应平台&#xff0c;前往此地址免费体验&#xff01; 告警路由 什么是告警路由&#xff1f; FlashDuty已经与Zabbix、Prometheus等监控系统实现无缝集成&#xff0c;通过一个简单的webhook就可以把告警系统产生的所有告警事件推送到FlashDut…

Simulink 模型简单加密

本文介绍的Simulink模型的简单加密方法&#xff0c;即简单禁止用户查看和修改模块内部结构&#xff0c;不对模型生成的源代码进行控制。如果想采用编译加密&#xff08;用户采用模型生成的代码也能进行加密控制&#xff09;&#xff0c;参见&#xff1a;Simulink模型编译加密共…

位图(bitset)和布隆过滤器

位图将数字映射到比特位上&#xff0c;用0&#xff0c;1来表示数据存在与否。 适用场景&#xff1a;大量数据(2^32次方约为40亿数据&#xff0c;0.5GB)&#xff0c;判断存在与否。 template<size_t N> class Bitset { public:Bitset(){// 在x86下size_t表示四个字节&am…

EM32DX-C1【分布式io】

1设备类型&#xff1a; 电压&#xff1a;DC24V 输入16点 输出16点雷赛 EM32DX-C1 模块是一款基于 ASIC 技术的高性能、高可靠性的 CANopen 总线数字 量输入输出扩展模块&#xff0c;具有 16 路通用输入接口和 16 路通用输出接口。输入输出接口均采用光 电隔离和…

规则引擎Drools使用,0基础入门规则引擎Drools(四)WorkBench控制台

文章目录 系列文章索引八、WorkBench简介与安装1、WorkBench简介2、安装 九、WorkBench使用方式1、创建空间2、创建项目3、创建数据对象4、创建DRL规则文件5、创建测试场景6、设置KieBase和KieSession7、编译、构建、部署8、在项目中使用部署的规则 系列文章索引 规则引擎Droo…

Spring Security 6.1.x 系列(6)—— 显式设置和修改登录态

一、前言 此篇是对上篇 Spring Security 6.1.x 系列&#xff08;5&#xff09;—— Servlet 认证体系结构介绍 中4.9章节显式调用SecurityContextRepository#saveContext进行详解分析。 二、设置和修改登录态 2.1 登录态存储形式 使用Spring Security框架&#xff0c;认证成…

创建可以离线打包开发的uniapp H5项目

安装node环境 略 安装vue脚手架&#xff0c;在线 npm install -g vue/cli PS&#xff1a;vue-cli已进入维护模式&#xff0c;vue3最新脚手架使用npm init vuelatest安装&#xff0c;安装后使用create-vue替换vue指令&#xff0c;create-vue底层使用vite提升前端开发效率&…

计算机视觉算法——基于Transformer的目标检测(DN DETR / DINO / Sparser DETR / Lite DETR)

计算机视觉算法——基于Transformer的目标检测&#xff08;DN DETR / DINO&#xff09; 计算机视觉算法——基于Transformer的目标检测&#xff08;DN DETR / DINO&#xff09;1. DN DETR1.1 Stablize Hungarian Matching1.2 Denoising1.3 Attention Mask 2. DINO2.1 Contrasti…

Qt5.15.2静态编译 VS2017 with static OpenSSL

几年前编译过一次Qt静态库:VS2015编译Qt5.7.0生成支持XP的静态库,再次编译,毫无压力。 一.环境 系统:Windows 10 专业版 64位 编译器:visual studio 2017 第三方工具:perl,ruby和python python用最新的3.x.x版本也是可以的 这三个工具都需要添加到环境变量,安装时勾选…

获取数据库中最占用内存的sql语句

SELECT TOP 20 total_worker_time/1000 AS [总消耗CPU 时间(ms)],execution_count [运行次数], qs.total_worker_time/qs.execution_count/1000 AS [平均消耗CPU 时间(ms)], last_execution_time AS [最后一次执行时间],min_worker_time /1000 AS [最小执行时间(ms…

桥梁道路结冰传感器守护出行安全的重要工具

随着冬季的到来&#xff0c;气温逐渐降低&#xff0c;路面和桥梁容易结冰&#xff0c;给人们的出行带来安全隐患。为了解决这一问题&#xff0c; WX-JB2H 桥梁道路结冰传感器应运而生。本文将详细介绍桥梁道路结冰传感器的作用、原理及在冬季出行中的重要性。 一、桥梁道路结冰…

Webhook端口中的自签名身份验证

概述 有时&#xff0c;可能需要通过 Webhook 端口从交易伙伴处接收数据&#xff0c;但该交易伙伴可能需要更多的安全性&#xff0c;而不仅仅是用于验证入站 Webhook 请求的基本身份验证用户名/密码 – 或者您可能只想在入站 Webhook 消息上添加额外的安全层。 使用 Webhook 端…

TikTok数据分析:如何通过数字洞察提升内容质量?

引言 TikTok作为全球最热门的短视频平台之一&#xff0c;每天吸引着亿万用户发布和观看各类内容。在这个充满创意的舞台上&#xff0c;内容质量成为吸引关注和提高曝光度的关键。 而要达到这一目标&#xff0c;数字数据分析成为不可或缺的工具。本文将深入探讨如何通过TikTok数…

Kotlin学习——kt里的集合,Map的各种方法之String篇

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

FaceChain集成最强开源SDXL,生成人像质感拉满!

一、介绍 FaceChain&#xff0c;一款备受欢迎的AI写真开源项目&#xff0c;目前已与最强大的开源生图模型SDXL完美融合&#xff01;这将为用户带来前所未有的高质量AI写真体验。 FaceChain是一个可以用来打造个人数字形象的深度学习模型工具。用户仅需要提供最低一张照片即可获…

[网络] 5. TCP 链接的建立与释放~汇总

大部分内容源于网络加之个人理解&#xff5e;巨人的肩膀有多大决定你可以看得多远&#xff5e; 文章目录 1. 三次握手说一下三次握手的过程为什么是三次握手 2. 四次挥手说一下四次挥手的过程为什么需要四次挥手有可能出现三次挥手吗&#xff0c;什么时候会出现呢&#xff1f;为…

【数据结构】堆的实现

目录 1. 前言2. 堆的实现2.1 初始化2.2 插入2.2.1 分析2.2.1.1 情况一2.2.1.2 情况二2.2.1.3 情况三 2.2.2 插入代码实现2.2.2.1 向上调整代码 2.3 删除2.3.1 分析2.3.2 删除代码实现2.3.2.1 向下调整代码 2.4 找根节点数据2.5 元素个数2.6 判空2.7 销毁 3. 源代码3.1 Heap.h3.…
最新文章