高精度+区间dp 洛谷P1005矩阵取数游戏

两点教训:

  1. #define 自定义的东西是原模原样的替换过去
    在某些情况要记得加括号
#define base  (1000*1000*1000)
    z.a[i]+= ( (x.a[i]+y.a[i])%base );
    z.a[i+1]+=(x.a[i]+y.a[i])/base;

如果不加括号的话,
就会变成

z.a[i]+= ( (x.a[i]+y.a[i])%1000*1000*1000);
z.a[i+1]+=(x.a[i]+y.a[i])/1000*1000*1000;

想要的运算顺序就不对了。
很容易忽视

  1. 关于输出自己写的高精度
    1)直到有非零数才开始输出,前导0不输出,而且本身为零也不输出!!!
    2)非零数之后的数才开始输出前导零
    3)在满足以上第一条的情况下,又会导致当这个 高精度的数 全部是0 的时候啥也不输出,所以又要特判
void print(ll ans){
bool have=0;
for(int i=3;i>=0;--i){
    if(have)
        printf("%09d",ans.a[i]);
    else if(i==0||ans.a[i])
        printf("%d",ans.a[i]);

    if(ans.a[i])have=1;

}
printf("\n");
}

这个题要爆long long
高精度

状态转移方程有点反人类
抓住两点

  1. 找到dp可以表示 我最优就是我最优,而不受其他的影响
    (虽然有点傻逼,甚至我自己也突然不知道是啥意思,但是 当我用其他的来表示的时候都不对,然后想到 这个正确的来表示就突然有这种感觉,这一段最优就是这一段最优)
    2.由少推多,而不是去想着分解
    由少推多 更容易找到解决办法

“当前”由有限的“少”推出来,“当前”不受“多”的影响

dp[i][j]=max(dp[i-1][j]+a[i-1]*x,dp[i][j+1]+a[j+1]*x);
#include<bits/stdc++.h>
#define go(i,a,b) for(int i=a;i<=b;++i)
#define LL long long
#define base  (1000*1000*1000)
using namespace std;
struct ll{
LL a[5];
ll(){memset(a,0,sizeof(a));}
};

ll operator + (const ll &x, const ll &y){

ll z;
go(i,0,3){
    z.a[i]+= ( (x.a[i]+y.a[i])%base );
    z.a[i+1]+=(x.a[i]+y.a[i])/base;

}

return z;

}


ll operator * (const LL &x, const ll &y){

ll z;

go(i,0,3){
    z.a[i]+= ( (x*y.a[i])%base );
    z.a[i+1]+=(x*y.a[i])/base;

}

return z;

}


void print(ll ans){
bool have=0;
for(int i=3;i>=0;--i){
    if(have)
        printf("%09d",ans.a[i]);
    else if(i==0||ans.a[i])
        printf("%d",ans.a[i]);

    if(ans.a[i])have=1;

}

printf("\n");


}




LL a[100+20];
ll dp[100+20][100+20];

ll max(ll x,ll y){
for(int i=3;i>=0;--i){
    if(x.a[i]>y.a[i])return x;
    if(x.a[i]<y.a[i])return y;

}

return x;
}


//for one line
ll f(int m){

go(i,1,m) scanf("%lld",&a[i]);

ll x;
x.a[0]=2;

for(int len=m-1;len>=1;--len,x=2LL*x){
    for(int i=1;i+len-1<=m;++i){
        int j=i+len-1;
        dp[i][j]=max(dp[i-1][j]+a[i-1]*x,dp[i][j+1]+a[j+1]*x);

    }


}

ll ans;
go(i,1,m){
    ans=max(ans,dp[i][i]+a[i]*x);

}

return ans;


}




void solve(){

int n,m;
ll ans;
cin>>n>>m;
go(i,1,n){
ans=ans+f(m);

}


print(ans);

return;
}






int main(){




int t=1;
//cin>>t;
++t;

while(--t){
    solve();

}




return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值