特殊问题 用两位long long 代替大数运算
原问题
矩阵取数【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m 的矩阵,矩阵中的每个元素aij均
为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分= 被取走的元素值*2i,
其中i 表示第i 次取数(从1 开始编号);
4. 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分
需要解决的大数运算
每次取一个数 X(0≤X≤1000)
总得分为 前面累积的得分 +X∗2i (1≤i≤80)
每次得分范围为 不超过 1000∗280 即 ≤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]范围为 0≤a[1]≤1019
至于a[0] 假设 0≤a[0]≤10i
我们需要 19+i≥30 (两个long long 能表示的范围要符合题目需要)
并且 10i∗1000≤1019 (确保最坏情况不溢出)
所以 11≤i≤16
至于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;
}