题意:

给定一个nm的矩阵,矩阵每个元素都有一个值a[i][j],由第一行开始向下走,每次取该行的行首或者行尾的一个元素,得分贡献为 所选元素2^k ,k表示第几次取数,由1开始,问得分最大值为多少?

分析:

由第一行开始走到最后一行,m个回合便可获得全部元素,因此对于每一行的取数对于其他行是无影响的,得分最大值即为每一行的最大值之和,而对于每一行的最大值,可以用dp简单地求出dp[l,r]=max(dp[l,r−1]∗2+a[r],dp[l+1,r]∗2+a[l])
每次的状态是由上一个状态取左边和取右边的答案最大值转移过来的。
当然这种状态转移也可以用记忆化搜索表示,比较直观好理解。
数据过大unsigned long long交了一发,发现只能过60,看来又要高精度了(此题坑点)
于是去题解找到了__int128_t 这黑科技的东西,用的真心爽。

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll __int128_t
ll n,m,i,j,x,y,ans;
ll a[85];
ll b[86];
ll dd[85][85][85];//记忆化数组
ll read()
{
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return f*x;
}
void print(ll x)
{
    if(x < 0) {putchar('-');x = -x;}
    if(x/10) print(x/10);
    putchar(x%10+'0');
}
ll dfs(ll l,ll r,ll k){//[l,r]区间取第一个数
    if(k>m) return 0;
    if(dd[l][r][k]) return dd[l][r][k];
    ll t1=b[k]*a[l]+dfs(l+1,r,k+1);//取左边第一个数
    ll t2=b[k]*a[r]+dfs(l,r-1,k+1);//取右边第一个数
    return dd[l][r][k]=max(t1,t2);
}
int main()
{
    n=read();
    m=read();
    b[1]=2;
    for(i=2;i<=m;i++){
        b[i]=b[i-1]*2;
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            a[j]=read();
        }
        ans+=dfs(1,m,1);
        memset(dd,0,sizeof(dd));
    }
    print(ans);
}