1166 矩阵取数游戏[区间dp+高精度]

时间:2021-07-30 16:44:07

1166 矩阵取数游戏

2007年NOIP全国联赛提高组

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
 
 
 
题目描述 Description

【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m 的矩阵,矩阵中的每个元素aij均
为非负整数。游戏规则如下:
1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分= 被取走的元素值*2i,
其中i 表示第i 次取数(从1 开始编号);
4. 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

输入描述 Input Description

第1行为两个用空格隔开的整数n和m。
第2~n+1 行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。

输出描述 Output Description

输出 仅包含1 行,为一个整数,即输入矩阵取数后的最大得分。

样例输入 Sample Input

2 3
1 2 3
3 4 2

样例输出 Sample Output

82

数据范围及提示 Data Size & Hint

样例解释

第 1 次:第1 行取行首元素,第2 行取行尾元素,本次得分为1*21+2*21=6
第2 次:两行均取行首元素,本次得分为2*22+3*22=20
第3 次:得分为3*23+4*23=56。总得分为6+20+56=82

【限制】
60%的数据满足:1<=n, m<=30, 答案不超过1016
100%的数据满足:1<=n, m<=80, 0<=aij<=1000

题解:个人觉得拿到60分就好,AC需要用高精度处理(特别恶心)。

60分代码(long long即可)

//2016/04/02 16:36:32
#include<bits/stdc++.h>
#define ref(i,x,y) for(long long i=x;i<=y;i++)
using namespace std;
long long f[][];
long long a[][];
long long xm[];
long long n,m,sum,ans;
int main(){
scanf("%d%d",&n,&m);xm[]=;
ref(i,,) xm[i]=xm[i-]<<;//2的i次幂
ref(i,,n)ref(j,,m)
scanf("%d",&a[i][j]);
ref(j,,n){
memset(f,,sizeof(f));//清零
ans=;
ref(i,,m) ref(x1,,i){//f[i][j]=max(f[i-1][j(当前列标-i)]+a[k(第几个)][i]*2^i,f[i][j-1]+a[k][m+1-j]*2^i)
long long x2=i-x1;
f[x1][x2]=max(f[x1-][x2]+a[j][x1]*xm[i],f[x1][x2-]+a[j][m-x2+]*xm[i]);
ans=max(f[x1][x2],ans);
}
sum+=ans;
}
cout<<sum<<endl;
return ;
}

AC代码:

1。INF进制版高精度+dp

#include <cstdio>
#define ref(i,x,y) for(int i=x;i<=y;i++)
#define INF 10000000000000000ll//考虑到long long 后边有011,19位数必须是素数
#define N 81
using namespace std;
struct node{//INF进制的高精度
long long num[];//19位压一组,压2组就好--最多27,、28位
}s,f[N][N],w,r;//w r 左右边
int a[N][N],n,m;
int main(){
scanf("%d%d",&n,&m);
ref(i,,n)
ref(j,,m)
scanf("%d",&a[i][j]),a[i][j]<<=;//先乘2 之后可以直接高精
s.num[]=0ll;//考虑与INF尾部匹配
s.num[]=0ll;
ref(l,,n){
ref(j,,m)
ref(k,,m){
f[j][k].num[]=0ll;
f[j][k].num[]=0ll;
}
ref(i,,m)
f[i][i].num[]=a[l][i];
ref(i,,m)
ref(j,,m-i+){
int k=j+i-;
w.num[]=0ll;
w.num[]=0ll;
r.num[]=0ll;
r.num[]=0ll;
w.num[]=f[j][k-].num[]*;
w.num[]=f[j][k-].num[]*;
if(w.num[]>=INF)
w.num[]%=INF,w.num[]++;
w.num[]+=a[l][k];
if(w.num[]>=INF)
w.num[]%=INF,w.num[]++;
r.num[]=f[j+][k].num[]<<1ll;
r.num[]=f[j+][k].num[]<<1ll;
if(r.num[]>=INF)
r.num[]%=INF,r.num[]++;
r.num[]+=a[l][j];
if(r.num[]>=INF)
r.num[]%=INF,r.num[]++;
f[j][k]=(w.num[]>r.num[]||w.num[]==r.num[]&&w.num[]>r.num[])?w:r;
}
s.num[]+=f[][m].num[];
s.num[]+=f[][m].num[];
if(s.num[]>=INF)
s.num[]%=INF,s.num[]++;
}
if(s.num[])//是否高于19位
printf("%lld%lld\n",s.num[],s.num[]);
else
printf("%lld\n",s.num[]);
return ;
}

2。 10进制版高精度 +dp

#include<bits/stdc++.h>
using namespace std;
#define N 90
int n,m;bool b;
int a[N],f[N][N][N];
int le[N],ri[N],s[N];//高精度处理数组
int main(){
scanf("%d%d",&n,&m);
for(int k=;k<=n;k++){
memset(a,,sizeof a);
memset(f,,sizeof f);
for(int i=;i<=m;i++) scanf("%d",&a[i]);
for(int i=;i<=m;i++) //i表示长度
for(int j=;j+i-<=m;j++){//j表示左端点
//f[j][j+i-1]=max(f[j][j+i-2]*2+a[j+i-1],f[j+1][j+i-1]*2+a[j]);
memset(le,,sizeof le);
memset(ri,,sizeof ri);
for(int t=;t<N;t++) le[t]=f[j][j+i-][t]*;
le[]+=a[j+i-];
for(int t=;t<N-;t++)//10进制高精度跑的有点慢
le[t+]+=le[t]/,le[t]%=;
for(int t=;t<N;t++) ri[t]=f[j+][j+i-][t]*;
ri[]+=a[j];
for(int t=;t<N-;t++)
ri[t+]+=ri[t]/,ri[t]%=;
int t1=N-,t2=N-;
while(le[t1]==&&t1!=) t1--;
while(ri[t2]==&&t2!=) t2--;
if(t1>t2) b=;
else if(t1<t2) b=;
else
for(int q=t2;q>=;q--)
if(le[q]>ri[q]){
b=;break;
}
else if(le[q]<ri[q]){
b=; break;
}
if(b)
for(int q=t1;q>=;q--) f[j][j+i-][q]=le[q];
else
for(int q=t2;q>=;q--) f[j][j+i-][q]=ri[q];
}
for(int i=;i<N;i++) s[i]+=f[][m][i];
}
for(int i=;i<N;i++) s[i]*=;
for(int i=;i<N-;i++){
s[i+]+=s[i]/;
s[i]%=;
}
int w=N-;
while(s[w]==&&w) w--;
for(int j=w;j>=;j--) printf("%d",s[j]);
return ;
}

对比:1166 矩阵取数游戏[区间dp+高精度]