[NOIP2007]方格取数游戏
做题时间:2021.03.26
\(【题目描述】\)
给定一个\(N\times M(N,M \leq 80)\)的矩阵,每次取数都从每一行的行首或行尾取走一个数,共取走\(N\)个数,第\(i\)次取走的数产生的得分为\(a_{i,j}\cdot 2^i\),问全部数取完后,最大得分是多少。
\(【输入格式】\)
2 3
1 2 3
3 4 2
\(【输出格式】\)
82
\(【考点】\)
区间DP、高精度
\(【做法】\)
由于行与行之间的取数顺序相互不影响,因此可以分开考虑。
由于每一次只能从当前行首级或行末取数,剩下的数构成一个连续的区间,可以考虑使用区间DP,定义\(f_{i,j}\)表示剩下区间\([i,j]\)没有取的最大得分。可以得出方程:
\[f_{i,j}=\max(f_{i-1,j}+a_{i}\cdot 2^{m-j+i-1},f_{i,j+1}+a_{j}\cdot 2^{m-j+i-1})
\]
当前行的答案即为:\(\max\limits_{1\leq i\leq m}(f_{i,i}+a_i\cdot 2^m)\)
最后加上高精度计算即可。
\(【代码】\)
#include<cstdio>
#include<iomanip>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e2;
string num[N][N];
string f[N][N],Pow[N];
int a[N],b[N],c[N];
int n,m;
inline int Max(int a,int b){return a>b?a:b;}
inline void Swap(char &a,char &b){char p=a;a=b;b=p;}
inline void Reverse(int x,int y)
{
int len=num[x][y].size();
for(int i=0;i<=len/2-1;i++) Swap(num[x][y][i],num[x][y][len-i-1]);
}
inline string Max(string sa,string sb)
{
int lena=sa.size();
int lenb=sb.size();
if(lena>lenb) return sa;
if(lenb>lena) return sb;
for(int i=lena-1;i>=0;i--){
if(sa[i]>sb[i]) return sa;
if(sb[i]>sa[i]) return sb;
}
return sa;
}
inline void Assign(string sa,string sb,int lena,int lenb)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
for(int i=0;i<lena;i++) a[i+1]=sa[i]-'0';
for(int i=0;i<lenb;i++) b[i+1]=sb[i]-'0';
}
inline string Add(string sa,string sb)
{
int lena=sa.size();
int lenb=sb.size();
Assign(sa,sb,lena,lenb);
int i=1,x=0,lenc=0;
string sc="";
while(i<=lena||i<=lenb){
c[i]=a[i]+b[i]+x;
x=c[i]/10;
c[i]%=10;
i++;
}
if(x) c[i]=x;
else i--;
lenc=i;
for(int i=1;i<=lenc;i++) sc+=c[i]+'0';
return sc;
}
inline string Mul(string sa,string sb)
{
string sc="";
int lena=sa.size();
int lenb=sb.size();
Assign(sa,sb,lena,lenb);
int x=0;
for(int i=1;i<=lena;i++){
x=0;
for(int j=1;j<=lenb;j++){
c[i+j-1]+=a[i]*b[j]+x;
x=c[i+j-1]/10;
c[i+j-1]%=10;
}
if(x) c[i+lenb]=x;
}
int lenc=lena+lenb;
while(c[lenc]==0&&lenc>1) lenc--;
for(int i=1;i<=lenc;i++) sc+=c[i]+'0';
return sc;
}
inline void Calc()
{
Pow[0]="1";
for(int i=1;i<=m;i++) Pow[i]=Mul(Pow[i-1],"2");
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>num[i][j],Reverse(i,j);
}
string ans="";
Calc();
for(int Line=1;Line<=n;Line++){
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++) f[i][j]="0";
}
for(int l=m-1;l>=1;l--){
for(int i=1;i<=m-l+1;i++){
int j=i+l-1;
f[i][j]=Max(Add(f[i-1][j],Mul(Pow[m-j+i-1],num[Line][i-1])),
Add(f[i][j+1],Mul(Pow[m-j+i-1],num[Line][j+1])));
}
}
string maxn="0";
for(int i=1;i<=m;i++){
maxn=Max(maxn,Add(f[i][i],Mul(Pow[m],num[Line][i])));
}
ans=Add(ans,maxn);
}
int ed=ans.size();
for(int i=ed-1;i>=0;i--) cout<<ans[i];
cout<<endl;
return 0;
}

浙公网安备 33010602011771号