矩阵取数游戏 2007年NOIP全国联赛提高组(dp+高精)

时间:2023-03-09 06:12:35
矩阵取数游戏  2007年NOIP全国联赛提高组(dp+高精)

矩阵取数游戏

2007年NOIP全国联赛提高组

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
题目描述 Description

【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的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 行,为一个整数,即输入矩阵取数后的最大得分。

样例输入 Sample Input

2 3
1 2 3
3 4 2

样例输出 Sample Output

82

数据范围及提示 Data Size & Hint

样例解释

第 1 次:第1 行取行首元素,第2 行取行尾元素,本次得分为1*21+2*21=6
第2 次:两行均取行首元素,本次得分为2*22+3*22=20
第3 次:得分为3*23+4*23=56。总得分为6+20+56=82

【限制】
60%的数据满足:1<=n, m<=30, 答案不超过1016
100%的数据满足:1<=n, m<=80, 0<=aij<=1000

/*
高精 恶心的我啊!
终于大概差不懂几乎弄懂了封装怎么压位
就是那个高精*低精调不出来,只能写高精*高精+压位
注意输出时先输出第一个然后再setfill setw时压几位就是几
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iomanip> #define N 81
#define ll long long
#define mod 10000 using namespace std;
ll n,m,k,cnt,flag;
ll a[N][N]; inline ll read()
{
ll x=,f=;char c=getchar();
while(c>''||c<''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} class Big{ public: int size,num[]; Big(){size=;memset(num,,sizeof num);}
Big(int data)
{
size=;
while(data!=){num[++size]=data%mod;data/=mod;}
}
inline void init(int data)
{
size=;
while(data!=){num[++size]=data%mod;data/=mod;}
}
inline void copy(Big A)
{
int s1=size,s2=A.size;size=s2;
for(int i=s2;i>=;i--) num[i]=A.num[i];
} };
Big f[N][N],Pow[N];
Big ans; ostream & operator << (ostream & os,Big A)
{
int s=A.size;
if(s==) os<<,flag=;
if(!flag)
{
os<<A.num[s];
for(int i=s-;i;i--) os<<setfill('')<<setw()<<A.num[i];
return os;
}
} Big operator + (Big A,Big B)
{
Big c;
int s=max(A.size,B.size);
c.size=s;
for(int i=;i<=s;i++) c.num[i]=A.num[i]+B.num[i];
for(int i=;i<=s;i++)
if(c.num[i]>=mod)
{
c.num[i+]+=c.num[i]/mod;
c.num[i]%=mod;
}
if(c.num[s+]!=) c.size++;
return c;
} Big operator * (Big A,Big B)
{
Big c;
int s1=A.size,s2=B.size;
for(int i=;i<=s1;i++)
for(int j=;j<=s2;j++)
{
c.num[i+j-]+=A.num[i]*B.num[j];
c.num[i+j]+=c.num[i+j-]/mod;
c.num[i+j-]%=mod;
}
c.size=A.size+B.size;
while(c.num[c.size]==) c.size--;
return c;
} bool operator > (Big A,Big B)
{
int s1=A.size,s2=B.size;
if(s1<s2) return ;
if(s1>s2) return ;
for(int i=s1;i>=;i--)
if(A.num[i]<B.num[i]) return ;
else if(A.num[i]>B.num[i]) return ;
return ;
} Big Cmax(Big A,Big B)
{
if(A>B) return A;
else return B;
} int main()
{
n=read();m=read();
for(int i=;i<=n;i++) for(int j=;j<=m;j++)
a[i][j]=read();
memset(Pow,,sizeof Pow);
Pow[].init();
for(int i=;i<=m;i++)
Pow[i].copy(*Pow[i-]);
for(int i=;i<=n;i++)
{
memset(f,,sizeof f);
for(int j=;j<=m;j++)
f[j][].copy(a[i][j]*Pow[m]);
for(int k=;k<=m;k++)
for(int j=;j<=m-k+;j++)
f[j][k].copy(Cmax(f[j][k-]+a[i][j+k-]*Pow[m-k+],f[j+][k-]+a[i][j]*Pow[m-k+])); ans.copy(ans+f[][m]);
}
cout<<ans;
return ;
}