OJ题号:洛谷1005
思路:
动态规划。
不难发现每行能够取得的最大值仅与当前行的数据有关,因此本题可以对每行的数据分别DP,最后求和。
设$f_{i,j}$表示左边取$i$个、右边取$j$个的最大值,则DP方程为$f_{i,j}=max(f_{i-1,j}+a_{i-1}*2^{i+j},f_{i,j-1}+a_{m-j}*2^{i+j})$。
然而数据规模较大,使用 int 只有40分,用 unsigned long long 只有60分。所以需要高精度,不过实现起来并不复杂。
另外有一些小小的优化,比如压位、预处理二的幂。
#include<cstdio>
#include<cstring>
#include<algorithm>
class BigInt {
private:
static const int k=;
int num[],len;
public:
BigInt() {
memset(num,,sizeof num);
len=;
}
BigInt(const int len,const int num) {
this->len=len;
this->num[]=num;
}
BigInt operator + (const BigInt &x) const {
BigInt ans;
for(int i=;i<=(ans.len=std::max(this->len,x.len));i++) {
ans.num[i]+=this->num[i]+x.num[i];
ans.num[i+]=ans.num[i]/k;
ans.num[i]%=k;
}
if(ans.num[ans.len+]) ans.len++;
return ans;
}
BigInt operator * (const int &x) const {
BigInt ans;
for(int i=;i<=(ans.len=this->len);i++) {
ans.num[i]+=this->num[i]*x;
ans.num[i+]=ans.num[i]/k;
ans.num[i]%=k;
}
if(ans.num[ans.len+]) ans.len++;
return ans;
}
bool operator < (const BigInt &x) const {
if(this->len<x.len) return true;
if(this->len>x.len) return false;
for(int i=this->len;i>=;i--) {
if(this->num[i]<x.num[i]) return true;
if(this->num[i]>x.num[i]) return false;
}
return false;
}
BigInt& operator = (const BigInt &x) {
this->len=x.len;
std::copy(&x.num[],&x.num[len+],this->num);
return *this;
}
void print() {
printf("%d",num[len]);
for(int i=len-;i>=;i--) {
printf("%04d",num[i]);
}
printf("\n");
}
};
const int M=;
BigInt pow[M]={BigInt(,)};
void calcpow(const int x) {
pow[x]=pow[x-]*;
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++) calcpow(i);
BigInt ans;
while(n--) {
int a[m];
BigInt f[m+][m+];
for(int i=;i<m;i++) scanf("%d",&a[i]);
memset(f,,sizeof f);
BigInt max;
for(int i=;i<=m;i++) {
for(int j=;j<=m-i;j++) {
if(i) f[i][j]=std::max(f[i][j],f[i-][j]+pow[i+j]*a[i-]);
if(j) f[i][j]=std::max(f[i][j],f[i][j-]+pow[i+j]*a[m-j]);
}
max=std::max(max,f[i][m-i]);
}
ans=ans+max;
}
ans.print();
return ;
}