【洛谷】【区间dp】【高精度】P1005 [NOIP2007 提高组] 矩阵取数游戏

简介: 【洛谷】【区间dp】【高精度】P1005 [NOIP2007 提高组] 矩阵取数游戏

题目描述
  帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n×m 的矩阵,矩阵中的每个元素 ai,j 均为非负整数。游戏规则如下:

每次取数时须从每行各取走一个元素,共 n个。经过 m次后取完矩阵内所有元素;
每次取走的各个元素只能是该元素所在行的行首或行尾;
每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 x2i,其中 i表示第 i次取数(从 1开始编号);
游戏结束总得分为 m次取数得分之和。
  帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

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

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

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

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

测试样例

输入
2 3
1 2 3
3 4 2

输出
82

解题思路
1.状态转移方程

  设fl[i]为左端点为l,右端点为l+len,第i行取数所能获得的最大值,则状态转移方程如下
f [ l ] [ l + l e n ] [ i ] = m a x ( 2 ∗ f [ l + 1 ] [ l + l e n ] [ i ] + 2 ∗ a [ i ] [ l ] , 2 ∗ f [ l ] [ l + l e n − 1 ] [ i ] + 2 ∗ a [ i ] [ l + l e n ] ) fl[i]=max(2fl+1[i]+2ai,2fl[i]+2ai)
fl[i]=max(2∗fl+1[i]+2∗ai,2∗fl[i]+2∗ai)

  可以化简为
f [ l ] [ l + l e n ] [ i ] = m a x ( f [ l + 1 ] [ l + l e n ] [ i ] + a [ i ] [ l ] , f [ l ] [ l + l e n − 1 ] [ i ] + a [ i ] [ l + l e n ] ) ∗ 2 fl[i]=max(fl+1[i]+ai,fl[i]+ai)*2
fl[i]=max(fl+1[i]+ai,fl[i]+ai)∗2

2.高精度的问题

  这个题目虽然每个数不是很大,但是最后的结果会超出64位,所以需要用到高精度,文末提供了两种高精度的方法,第一种是用__int128,第二种是自己写一个高精度模板。

3.一定要判断全0的情况,否则会有一个测试样例过不去。

AC代码(__int128)

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

#define ll __int128
#define MAX 85
//__int128的输出
void print(ll x)
{
    if(x>9) print(x/10);
    putchar(x%10 + '0');
}
//__int128的输入
void input(ll &s)
{
    s = 0;
    char c = ' ';
    while(c<'0' || c>'9') c = getchar();
    while(c>='0' && c<='9')
    {
        s = s*10+c-'0';
        c = getchar();
    }
}

int main()
{
    int n,m;
    ll sum = 0;
    cin>>n>>m;
    ll a[MAX][MAX];
    ll fun[MAX][MAX][MAX] = {0};
    //读入数据
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            input(a[i][j]);
        }
    }
    //开始进行计算,区间dp
    for(int i=1;i<=n;i++)
    {
        for(int len=0;len<m;len++)
        {
            for(int l=1;l<=m;l++)
            {
                fun[l][l+len][i] = max((fun[l+1][l+len][i]+a[i][l]),(fun[l][l+len-1][i]+a[i][l+len])) * 2;
            }
        }
    }
    //计算最后结果,高精度
    for(int i=1;i<=n;i++)
    {
        sum += fun[1][m][i];
    }
    print(sum);
    return 0;
}

AC代码(自定义高精度模板)

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

#define MAX 85
long long p = 1e18;
//定义128位整数
struct int128
{
    long long high;
    long long low;
};
//取最大值
int128 max(int128 a,int128 b)
{
    if(a.high > b.high) return a;
    else if(a.high < b.high) return b;
    else if(a.low > b.low) return a;
    else if(a.low < b.low) return b;
    else return a;
}
//128位+128位
int128 operator + (int128 a,int128 b)
{
    int128 k;
    k.high = 0;
    k.low = 0;
    k.low = a.low + b.low;
    k.high = k.low / p + a.high + b.high;
    k.low = k.low % p;
    return k;
}
//128位*64位
int128 operator * (int128 a,int b)
{
    int128 k;
    k.high = 0;
    k.low = 0;
    k.low = a.low * b;
    k.high = k.high + k.low / p + a.high * b;
    k.low = k.low % p;
    return k;
}

int main()
{
    int n,m;
    int128 num[MAX][MAX],fun[MAX][MAX][MAX],sum;
    scanf("%d%d",&n,&m);
    fun[MAX][MAX][MAX];
    //读入数据
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%lld",&num[i][j].low);
        }
    }
    //区间dp
    for(int i=1;i<=n;i++)
    {
        for(int len=0;len<m;len++)
        {
            for(int l=1;l<=m;l++)
            {
                fun[l][l+len][i] = max(fun[l+1][l+len][i]+num[i][l],fun[l][l+len-1][i]+num[i][l+len]) * 2;
            }
        }
    }

    for(int i=1;i<=n;i++)
    {
        sum = sum + fun[1][m][i];
    }
    //注意低位如果位数不够要补零
    if(sum.high == 0) printf("%lld",sum.low);
    else printf("%lld%018lld",sum.high,sum.low);
    return 0;
}
相关文章
|
7月前
|
Serverless
P1149 [NOIP2008 提高组] 火柴棒等式
P1149 [NOIP2008 提高组] 火柴棒等式
|
6月前
【洛谷 P1035】[NOIP2002 普及组] 级数求和 题解(循环)
**NOIP2002普及组题目:求级数$S_n=1+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n}$超过$k$的最小$n$。给定$1\leq k\leq 15$,输出满足$S_n&gt;k$的$n$。输入$1$个整数$k$,输出相应$n$。例如,输入$1$,输出$2$。代码中使用double确保精度,通过累加求和判断条件找到$n$。**
46 0
|
6月前
|
C++
【洛谷 P1042】[NOIP2003 普及组] 乒乓球 题解(模拟+向量)
`NOIP2003`普及组编程题:乒乓球比赛模拟。给定一系列球赛记录(WL序列),程序需按11分和21分制分析比分。输入含多个字符串,含W(华华得分)、L(对手得分)和E(结束标记)。输出每局比分,分制间空行间隔。样例:`WWWWWW...` → `11:0\n11:0\n1:1`(11分制)和`21:0\n2:1`(21分制)。代码使用C++,逐字符读取,当分差≥2且得分≥x时输出比分。
67 0
|
6月前
|
机器学习/深度学习 人工智能
【洛谷 P1028】[NOIP2001 普及组] 数的计算 题解(递推)
在NOIP2001普及组的数的计算题目中,给定自然数`n`,需构造遵循特定规则的合法数列。合法序列始于`n`,新元素不超过前一项的一半。任务是找出所有这样的数列数量。例如,当`n=6`时,合法序列包括`6`, `6,1`, `6,2`, `6,3`, `6,2,1`, `6,3,1`。程序通过动态规划求解,当`i`为奇数时,`a[i] = a[i - 1]`;为偶数时,`a[i] = a[i - 1] + a[i / 2]`。代码中预处理数组`a`并输出`a[n]`作为答案。输入`n`后,程序直接计算并打印合法数列个数。
77 0
|
6月前
【洛谷 P1088】[NOIP2004 普及组] 火星人 题解(全排列+向量)
**火星人问题摘要:** NOIP2004普及组竞赛中的题目,涉及火星人用手指的排列表示数字。人类需计算火星人数字与给定数值之和的新排列。给定火星人手指数N(≤10000),加上的数M(≤100),以及初始排列,要求输出新排列。30%的数据中N≤15,60%的数据中N≤50。使用`next_permutation`函数找到第M个排列。样例:N=5, M=3, 初始排列1 2 3 4 5,输出1 2 4 5 3。
65 0
|
6月前
|
存储
【洛谷 P2141】[NOIP2014 普及组] 珠心算测验 题解(集合+多重循环)
**NOIP2014普及组的珠心算测验题要求参赛者找出给定集合中多少个数可表示为其他两个不同数的和。输入含n个正整数,输出满足条件的数的个数。样例输入4个数,输出2,因1+2=3且1+3=4。代码利用集合存储和,遍历所有数对组合,当找到匹配和时插入集合,最后输出集合大小。注意数据规模为n≤100,数不超过10,000。**
141 0
|
6月前
【洛谷 P2669】[NOIP2015 普及组] 金币 题解(循环)
`NOIP2015`普及组题目,骑士按周期领金币:第一天1枚,随后$n$天每天$n$枚,然后$n+1$天每天$n+1$枚。给定天数$k$,求总金币数。输入$k$,输出金币总数。样例输入6,输出14;输入1000,输出29820。代码使用循环和变量控制周期,累计金币数。
134 0
|
7月前
|
存储 算法 C++
第 284 场周赛(C++ | 枚举 | 分类讨论 | 最短路 | 建反图)
【4月更文挑战第1天】- [LeetCode 6031](https://leetcode-cn.com/problems/find-all-k-distant-indices-in-an-array/):给定数组 `nums`、键值 `key` 和距离 `k`,找到所有与键值相等且与任意下标距离不超过 `k` 的下标,返回升序排序的列表。找到最小权重。
46 0
|
人工智能 索引
洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏
洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏
|
存储
蓝桥杯19国赛-矩阵计数
蓝桥杯19国赛-矩阵计数
92 0