Codevs1166 矩阵取数游戏

  • 首先吐槽:先是高精加忘了进位时要用+=,WA了几发;后来是高精乘单精写不对,被迫写高精乘高精,这样一来又WA了几发;高精写好后,发现居然T了,于是改成多压几位(多压几位也是可以节省时间的),然而还是T,并没有什么L用;于是再次发现,我居然用了O( nm3 )的算法,被迫改为O( nm2 );于是乎,不T了,WA了三个点;最后发现,改算法时我把区间长度当成了n而不是m(就这还能过7个点我也是醉了),于是,就AC了……

  • 说一下思路:区间DP,状态转移方程为f[i][j]=2*max{f[i+1][j]+a[i],f[i][j-1]+a[j]},目标f[1][m],预处理f[i][i]=2*a[i]。这个意思这是,我们不必非要从两头开始取(即从外往里取),从里往外取,每次取乘2也可以达到同样的效果,这样就省去了对于 2i 的计算。在枚举时,我刚开始的思路是,用k确定最内层的元素(即最后取走的那一个),再利用i从k到1、j从k到m进行扩展,但这样的做法是O( nm3 ),加上高精就会T,于是得到这样的一个思路,就是用k枚举区间长度从小到大(这样可以保证在计算长度为k的区间时所要用到的长度为k-1的区间值均已计算过),用i枚举区间左端点,这样区间右端点j就可以得出了,即j=i+k-1,不用再花费O(m)的时间去枚举右端点了,这样时间复杂度优化为O( nm2 )。当然空间上也是可以优化的,由于每行数是相互独立的,我们可以每次只读取一行进行处理。

  • 代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct bignum
{
       long long len,a[105];
       bignum()
       {
               len=1;
               memset(a,0,sizeof(a));
       }
       bignum (long long num)
       {
              *this=num;
       }
       bignum operator + (const bignum &b)
       {
              bignum c;
              c.len=max(len,b.len);
              for (long long i=1;i<=c.len;++i)
              {
                  c.a[i]+=a[i]+b.a[i];
                  c.a[i+1]+=c.a[i]/100000000;
                  c.a[i]%=100000000;
              }
              if (c.a[c.len+1]>0)
                c.len++;
              return c;
       }
       bignum operator += (const bignum &b)
       {
              *this=*this+b;
              return *this;
       }
       bignum operator * (const bignum &two)
       {
              bignum b;     
              long long i,j;
              b.len=len+two.len;
              for (i=1;i<=len;++i)
                for (j=1;j<=two.len;++j)
                {
                     b.a[i+j-1]+=a[i]*two.a[j];
                     b.a[i+j]+=b.a[i+j-1]/100000000;
                     b.a[i+j-1]%=100000000;
                }
              while (!b.a[b.len] && b.len>1)
                 b.len--;
              return b;
       }
       bignum operator *= (const bignum &a)
       {
              *this=(*this)*a;
              return *this;
       }
       bool operator < (const bignum &b) const
       {
              if (len!=b.len)
                return len<b.len ? 1:0 ;
              for (long long i=len;i>=1;--i)
                if (a[i]!=b.a[i])
                  return a[i]<b.a[i] ? 1:0 ;
              return 1;
       }
       bignum operator = (long long num)
       {
              memset(a,0,sizeof(a));
              len=0;
              while (num!=0)
              {
                    a[++len]=num%10000000;
                    num/=10000000;
              }
              return *this;
       }    
};
long long n,m;
bignum na[85];
bignum ans,f[85][85];
bignum two=2;

void print(bignum x)
{
     printf("%lld",x.a[x.len]);
     for (int i=x.len-1;i>=1;--i)
       printf("%08lld",x.a[i]);

}

void dp()
{
     long long i,j,k;
     for (k=2;k<=m;++k)
       for (i=1;i<=m;++i)
       {
           if (i+k>m+1)
             continue;
           j=i+k-1;
           bignum a1=f[i+1][j]+na[i];
           bignum a2=f[i][j-1]+na[j];
           f[i][j]=max(a1,a2);
           f[i][j]*=two;
       }
     ans+=f[1][m];
}

void init()
{
     scanf("%lld%lld",&n,&m);
     for (long long i=1;i<=n;++i)
     {
         for (long long j=1;j<=m;++j)
         {
             long long tmp;
             scanf("%lld",&tmp);
             na[j]=tmp;
             f[j][j]=na[j]*two;
         }
         dp();
     }  
     print(ans);
}

int main()
{
    init();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值