特殊问题 用两位long long 代替大数运算

特殊问题 用两位long long 代替大数运算

原问题

矩阵取数【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m 的矩阵,矩阵中的每个元素aij均
为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分= 被取走的元素值*2i,
其中i 表示第i 次取数(从1 开始编号);
4. 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分

需要解决的大数运算

每次取一个数 X(0X1000)
总得分为 前面累积的得分 +X2i (1i80)
每次得分范围为 不超过 1000280 1028
最多取80次计算最后得分的范围为 答案 1030

思路

用 long long a[2] 存放 表示已经积累的分数
用 long long b[2] 存放 上次的 2i
用 long long c[2] 存放这次的 分数

计算

  • 1.算出此次得分
b[1]=b[1]*2;
b[0]=b[0]*2;
if(b[0]>Limit)
{
    b[1]++;           //这里进位只可能是1
    b[0]%=Limit;
}
//b[2] 等于这次的2^i
c[1]=b[1]*x;
c[0]=b[0]*x;
if(c[0]>Limit)
{
    c[1]+=c[0]/Limit; //这里进位有可能大于1
    c[0]%=Limit; 

}
//乘上x
  • 1.加到总得分
a[1]+=c[1];
a[0]+=c[0];
if(a[0]>limit)
{
    a[1]++;         //这里进位只可能是1
    a[0]=a[0]%Limit;
}

常数LImit应该为多少

64位的a[1] 可以设定a[1]范围为 0a[1]1019
至于a[0] 假设 0a[0]10i
我们需要 19+i30 (两个long long 能表示的范围要符合题目需要)
并且 10i10001019 (确保最坏情况不溢出)
所以 11i16
至于Limit 应该取 10i

代码

/*
【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m 的矩阵,矩阵中的每个元素aij均
为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分= 被取走的元素值*2i,
其中i 表示第i 次取数(从1 开始编号);
4. 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

输入描述 Input Description
第1行为两个用空格隔开的整数n和m。
第2~n+1 行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。

输出描述 Output Description
输出 仅包含1 行,为一个整数,即输入矩阵取数后的最大得分。
*/
#include<cstdio>
#include<iostream>
#include<string.h>
#include<string>
#include<cmath>
#pragma warning(disable:4996)
using namespace std;
class bign
{
public:
    long long high;
    long long low;
    friend ostream& operator<<(ostream& out, bign &a)
    {
        if (a.high)
        {
            out << a.high;
            printf("%06d", a.low / 1000000);
            printf("%06d", a.low % 1000000);
        }
        else
        {
            cout << a.low;
        }
        return out;
    }
};
int n, m;
int matrix[81][81];
bign dp[81][81][81];
bign two[81];       //存2^i
long long Limit = 1000000000000;//10^12
bign add(bign a, bign b)
{
    a.high += b.high;
    a.low += b.low;
    if (a.low > Limit)
    {
        a.high++;
        a.low %= Limit;
    }
    return a;
}
bign mul(bign a, int b)//b<=1000
{
    a.high *= b;
    a.low *= b;
    if (a.low > Limit)
    {
        a.high += a.low / Limit;
        a.low %= Limit;
    }
    return a;
}

bool ismax(bign a, bign b)
{
    if (a.high > b.high || (a.high == b.high&& a.low > b.low))return 1;
    return 0;
}
int main()
{
    //n行m列
    cin >> n >> m;
    int i, j;
    for (i = 1; i <= n; i++)
    {
        for (j = 1; j <= m; j++)
        {
            cin >> matrix[i][j];
        }
    }
    memset(dp, 0, sizeof(dp));
    two[1].high = 0;
    two[1].low = 2;
    for (i = 2; i <= m; i++) //计算2^i
    {
        two[i] = mul(two[i - 1], 2);
        //cout << two[i] << endl;

    }



    for (i = 1; i <= n; i++) //计算每一行
    {
        int head;
        for (head = m; head >=1; head--)
        {
            int end;
            for (end = head; end <=m; end++)
            {
                int NumberOfTimes = m-(end - head + 1)+1;
                if (head == end)
                {
                    dp[i][head][end] = mul(two[NumberOfTimes], matrix[i][head]);
                }
                else
                {
                    bign a = add(dp[i][head + 1][end],mul(two[NumberOfTimes],matrix[i][head]));
                    bign b = add(dp[i][head][end - 1], mul(two[NumberOfTimes], matrix[i][end]));
                    dp[i][head][end] = ismax(a, b) ? a : b;
                }

            }
        }
    }
    bign sum = bign{ 0,0 };
    for (i = 1; i <= n; i++)//累加每一行
    {
        sum = add(sum, dp[i][1][m]);
    }
    cout << sum;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值