题目描述:
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的(n imes m)的矩阵,矩阵中的每个元素(a(i,j))均为非负整数。
游戏规则如下:
1.每次取数时须从每行各取走一个元素,共(n)个。经过(m)次后取完矩阵内所有元素;
2.每次取走的各个元素只能是该元素所在行的行首或行尾;
3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值( imes 2^i) ,其中(i)表示第(i)次取数(从(1)开始编号);
游戏结束总得分为(m)次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
思路:
看上去是一个矩阵问题,但是实际上,我们可以发现,每一行是相互独立的,也就是说,我们不要从“次”上来研究,而是要根据“行”来研究,最后把每一行的结果相加就可以了。
单独看一行,这个问题就简化成了:有一个序列,只能从两头拿,每次拿的得分为物品值乘上一个权值(和次数有关),要求最终得分最大。
是不是很熟悉?对了,和这道题异曲同工:https://www.luogu.com.cn/problem/P2858 没做过这道题的同学可以先做一下这个简化版。
本题是区间DP老套路了,分从头拿和从尾拿可以分两种情况转移:
(dp[i][j]=max(a[i] * (1<<m-(j-i)) + dp[i+1][j] , a[j] * (1<<m-(j-i)) + dp[i][j-1])), dp[i][j]表示剩下i-j个数时之后能取得的最大值。
然后可以写出这样的程序:
#include<cstdlib>
#include<algorithm>
#define ull unsigned long long
using namespace std;
ull a[81][81],dp[81][81][81];
int main(){
int i,j,n,m,k;
ull ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
dp[i][j][j]=a[i][j]*(1ll<<m);
for(k=j-1;k;k--){
dp[i][k][j]=max(a[i][k]*(1ll<<m-(j-k))+dp[i][k+1][j],a[i][j]*(1ll<<m-(j-k))+dp[i][k][j-1]);
}
}
}
for(i=1;i<=n;i++)
ans+=dp[i][1][m];
printf("%lld",ans);
return 0;
}
然后我们就A了这道题。。。
。。。
。。。
吗?
交上去之后发现毒瘤出题人要求高精度,那就重写吧,但是可读性来说绝对是上面的好。
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
struct BigInt{
int b[50],rear;
} dp[81][81][81],a[81][81];
void init(BigInt &x){
for(int i=0;i<50;i++){
x.b[i]=0;
}
x.rear=0;
return;
}
BigInt operator +(const BigInt x,const BigInt y){
int flag=0,i;BigInt ans;
init(ans);
for(i=0;i<=max(x.rear,y.rear);i++){
ans.b[i]=x.b[i]+y.b[i]+flag;
flag=ans.b[i]/10;
ans.b[i]%=10;
}
if(flag){
ans.b[i]=1;
ans.rear=1+max(x.rear,y.rear);
}
else ans.rear=max(x.rear,y.rear);
return ans;
}
BigInt operator *(const BigInt x,const BigInt y){
int flag=0,i,j;BigInt ans;
init(ans);
for(i=0;i<=x.rear;i++){
for(j=0;j<=y.rear;j++){
ans.b[i+j]+=x.b[i]*y.b[j];
}
}
ans.rear=x.rear+y.rear;
for(i=0;i<=x.rear+y.rear;i++){
ans.b[i]+=flag;
flag=ans.b[i]/10;
ans.b[i]%=10;
}
ans.rear=i;
if(flag) ans.b[i]=flag;
else ans.rear--;
return ans;
}
BigInt tran(char x[50]){
BigInt y;
int l=strlen(x)-1;
for(int i=0;i<=l;i++)
y.b[l-i]=x[i]-'0';
y.rear=l;
return y;
}
BigInt Max(BigInt x,BigInt y){
if(x.rear>y.rear) return x;
else if(y.rear>x.rear) return y;
else{
for(int i=x.rear;i>=0;i--){
if(x.b[i]>y.b[i]) return x;
else if(y.b[i]>x.b[i]) return y;
}
}
return x;
}
void print(BigInt x){
for(int i=x.rear;i>=0;i--)
printf("%d",x.b[i]);
}
BigInt pow[81],two;
int main(){
int i,j,n,m,k;
BigInt ans;
char x[50];
init(ans);
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++){
scanf("%s",x);
a[i][j]=tran(x);
}
pow[0].b[0]=1;pow[0].rear=0;
two.rear=0;two.b[0]=2;
for(i=1;i<=m;i++){
pow[i]=two*pow[i-1];
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
dp[i][j][j]=a[i][j]*pow[m];
for(k=j-1;k;k--){
dp[i][k][j]=Max(a[i][k]*pow[m-(j-k)]+dp[i][k+1][j],a[i][j]*pow[m-(j-k)]+dp[i][k][j-1]);
}
}
}
for(i=1;i<=n;i++)
ans=ans+dp[i][1][m];
print(ans);
return 0;
}
高精度题最好用结构体写,函数封装一下,可以使代码更简洁。