矩阵取数游戏 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=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

class Big{
    
    public:
        
        int size,num[201];
        
        Big(){size=0;memset(num,0,sizeof num);}
        Big(int data)
        {
            size=0;
            while(data!=0){num[++size]=data%mod;data/=mod;}
        }
        inline void init(int data)
        {
            size=0;
            while(data!=0){num[++size]=data%mod;data/=mod;}
        }
        inline void copy(Big A)
        {
            int s1=size,s2=A.size;size=s2;
            for(int i=s2;i>=1;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==0) os<<0,flag=1;
    if(!flag)
    {
        os<<A.num[s];
        for(int i=s-1;i;i--) os<<setfill('0')<<setw(4)<<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=1;i<=s;i++) c.num[i]=A.num[i]+B.num[i];
    for(int i=1;i<=s;i++)
      if(c.num[i]>=mod) 
      {
            c.num[i+1]+=c.num[i]/mod;
            c.num[i]%=mod;
      }
    if(c.num[s+1]!=0) c.size++;
    return c;
}

Big operator * (Big A,Big B)
{
    Big c;
    int s1=A.size,s2=B.size;
    for(int i=1;i<=s1;i++)
      for(int j=1;j<=s2;j++)
        {
            c.num[i+j-1]+=A.num[i]*B.num[j];
            c.num[i+j]+=c.num[i+j-1]/mod;
            c.num[i+j-1]%=mod;
        }
    c.size=A.size+B.size;
    while(c.num[c.size]==0) c.size--;
    return c;
}

bool operator > (Big A,Big B)
{
    int s1=A.size,s2=B.size;
    if(s1<s2) return 0;
    if(s1>s2) return 1; 
    for(int i=s1;i>=1;i--)
      if(A.num[i]<B.num[i]) return 0;
      else if(A.num[i]>B.num[i]) return 1;
    return 0;
}

Big Cmax(Big A,Big B)
{
    if(A>B) return A;
    else return B; 
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) 
      a[i][j]=read();
    memset(Pow,0,sizeof Pow);
    Pow[1].init(2);
    for(int i=2;i<=m;i++) 
      Pow[i].copy(2*Pow[i-1]);
    for(int i=1;i<=n;i++)
    {        
        memset(f,0,sizeof f);
        for(int j=1;j<=m;j++) 
          f[j][1].copy(a[i][j]*Pow[m]);
          for(int k=2;k<=m;k++)
            for(int j=1;j<=m-k+1;j++)
             f[j][k].copy(Cmax(f[j][k-1]+a[i][j+k-1]*Pow[m-k+1],f[j+1][k-1]+a[i][j]*Pow[m-k+1]));
              
        ans.copy(ans+f[1][m]);
    }
    cout<<ans;
    return 0;
}
折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
原文地址:https://www.cnblogs.com/L-Memory/p/7704012.html