[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;
}
posted @ 2021-04-01 21:01  lxzy  阅读(62)  评论(0)    收藏  举报