题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
1.每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
4.游戏结束总得分为m次取数得分之和。
想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式:
输入文件game.in包括n+1行:
第1行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。
数据范围:
60%的数据满足:1<=n, m<=30,答案不超过10^16
100%的数据满足:1<=n, m<=80,0<=aij<=1000
根本不想写高精度。。首先,状态我们怎么定义。取i次好像不太好写。前i个 以i结尾好像都不太好写。
我们想,取数的时候要么取走最右边的,要么取走最左边的,所以状态或许跟左右有关?
f[i][j]表示最左边是第i个数,最右边是第j个数的状态得分最大值。然后瞎推一下就好了。懒得写高精度去复制了int128模板,但是NOIP不让用0.0。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e2+2;
__int128 ma[MAXN][MAXN];
__int128 dp[MAXN][MAXN][MAXN],ans,sum=0;
__int128 poww[MAXN];
//dp[i][j][l]=max(dp[i-1][j]+(1<<k)*w[i-1],dp[i][j+1]+(1<<k)*w[j+1]);
//dp[][]
void Print(__int128 x)
{
if(x==0) return;
else if(x) Print(x/10);
putchar(x%10+'0');
}
int n,m;
int main(){
memset(dp,0,sizeof(dp));
memset(ma,0,sizeof(ma));
memset(poww,0,sizeof(poww));
scanf("%d%d",&n,&m);
poww[0]=1;
for(int i=1;i<=m;i++)poww[i]=poww[i-1]*2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&ma[i][j]);
}
for(int l=1;l<=n;l++)
for(int i=1;i<=m;i++)
for(int j=m;j>=i;j--){
dp[i][j][l]=max(dp[i-1][j][l]+poww[m-j+i-1]*ma[l][i-1],dp[i][j+1][l]+poww[m-j+i-1]*ma[l][j+1]);
}
for(int l=1;l<=n;l++){
ans=-1;
for(int i=1;i<=m;i++){
ans=max(ans,dp[i][i][l]+poww[m]*ma[l][i]);//+(1<<(m-1))*ma[l][i]
}
sum+=ans;
}
if(ans==0) printf("0");
else Print(sum);
return 0;
}