矩阵取数
Description
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n·m的矩阵,矩阵中的每个元素aij据为非负整数。游戏规则如下:
- 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有的元素;
- 每次取走的各个元素只能是该元素所在行的行首或行尾;
- 每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分 = 被取走的元素值*2i,其中i表示第i次取数(从1开始编号);
- 游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
Analysis
因为每行取数的策略可能不同,所以应该分行进行动规讨论。每行都要进行m次取数,每次取数的权值都是上一次的二倍。因为取数的分数是累计的,所以每次取完数后剩下部分也要求出最优策略,具备最优解子结构。那么
dp[i][j]=2*max(s[i]+dp[i+1][j],s[j]+dp[i][j-1])
到这里还没有结束,因为m<=80,所以最大是2^80,约合30位,所以高精度计算。
很坑的地方:因为状态转移中存在do[i+1][j],所以可能会数组越界,不能开到81。
Code
#include <bits/stdc++.h>
int n,m,s[1001];
struct bign{
int len,num[101];
bign operator = (bign eq){
len=eq.len;
memset(num,0,sizeof(num));
for(int i=0;i<len;i++)
num[i]=eq.num[i];
return *this;
}
bign operator + (bign ad){
bign ans;
ans.len=std::max(len,ad.len);
memset(ans.num,0,sizeof(ans.num));
int accu=0;
for(int i=0;i<ans.len;i++){
int res=num[i]+ad.num[i]+accu;
ans.num[i]=res%10;
accu=res/10;
}
if(accu)ans.num[ans.len++]=accu;
return ans;
}
bign operator + (int ad){
bign eq;
eq.len=0;
memset(eq.num,0,sizeof(eq.num));
while(ad){
eq.num[eq.len++]=ad%10;
ad/=10;
}
return *this+eq;
}
bign operator += (bign ad){
return *this=*this+ad;
}
bign operator += (int ad){
return *this=*this+ad;
}
bool operator > (bign ad){
if(len>ad.len)return 1;
if(len<ad.len)return 0;
for(int i=len-1;i>=0;i--)
if(num[i]>ad.num[i])return 1;
else if(num[i]<ad.num[i])return 0;
return 0;
}
friend std::ostream& operator << (std::ostream& out,bign ans){
for(int i=ans.len-1;i+1;i--)
out<<ans.num[i];
return out;
}
}ans,dp[81][81];
int main(){
freopen("game.in","r",stdin);
freopen("game.ans","w",stdout);
std::cin>>n>>m;
while(n--){
for(int i=1;i<=m;i++)
std::cin>>s[i];
for(int l=1;l<=m;l++)
for(int b=1;b<=m-l+1;b++){
int e=b+l-1;
bign la,ra;
la=dp[b+1][e]+s[b];
ra=dp[b][e-1]+s[e];
dp[b][e]=la>ra?la+la:ra+ra;
}
ans+=dp[1][m];
}
std::cout<<ans<<std::endl;
return 0;
}