帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素a[i,j] 均为非负整数。游戏规则如下:
每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
每次取走的各个元素只能是该元素所在行的行首或行尾;
每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
游戏结束总得分为m次取数得分之和。帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
正解 D P + DP+ DP+高精度
首先考虑 D P DP DP,每行答案互不影响,所以可以对每行单独 D P DP DP,把每行的答案加起来即可
这里可以从外到内 D P DP DP,也可以从内到外 D P DP DP
如果是从内到外 D P DP DP的话,越往后取数数字越大,所以每次都是原来数字之和 × 2 + ×2+ ×2+现取数,不用预处理 2 2 2的幂
可以容易得到 60 p t s 60pts 60pts的转移方程 f [ i ] [ j ] = m a x ( f [ i + 1 ] [ j ] ∗ 2 + f [ i ] [ i ] , f [ i ] [ j − 1 ] ∗ 2 + f [ j ] [ j ] ) f[i][j]=max(f[i+1][j]*2+f[i][i],f[i][j-1]*2+f[j][j]) f[i][j]=max(f[i+1][j]∗2+f[i][i],f[i][j−1]∗2+f[j][j])
这个方程再套上一个高精度的板子即可 A C AC AC
其实这题还有个 t r i c k trick trick,答案的范围不会超过__ i n t 128 int128 int128,可以直接用这个,时空完爆高精度
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include
#include
#include
#define MAXN 85
#define ll __int128
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))
using namespace std;
ll f[MAXN][MAXN];
ll a[MAXN];
ll n,m,ans;
O3 inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
O3 inline void print(ll x)
{
if (x<0)putchar('-'),x=-x;
if (x>9)print(x/10);
putchar(x%10+'0');
}
O3 int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n=read(),m=read();
fo(k,1,n)
{
memset(f,0,sizeof(f));
fo(i,1,m)a[i]=read(),f[i][i]=a[i]<<1;
fo(x,2,m)
{
fo(i,1,m)
{
ll j=i+x-1;
if (j>m)continue;
f[i][j]=max(f[i+1][j]*2+f[i][i],f[i][j-1]*2+f[j][j]);
}
}
ans=ans+f[1][m];
}
print(ans);
printf("\n");
return 0;
}
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include
#include
#include
#define MAXN 85
#define MAXLEN 1000
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define O3 __attribute__((optimize("-O3")))
using namespace std;
ll a[MAXN];
ll n,m;
O3 inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
struct BIGNUM
{
ll num[MAXLEN],len;
BIGNUM()
{
memset(num,0,sizeof(num)),len=1;
}
BIGNUM operator=(const char s[])
{
len=strlen(s);
fo(i,0,len-1)num[i]=s[len-i-1]-'0';
while (num[len-1]==0 && len>1)--len;
return *this;
}
BIGNUM operator=(const ll x)
{
ll temp=x;
len=1;
do
{
num[len-1]=temp%10;
temp/=10,len++;
}while (temp);
while (num[len-1]==0 && len>1)--len;
return *this;
}
BIGNUM operator+(const BIGNUM &x)const
{
BIGNUM temp;
temp.len=max(len,x.len)+1;
fo(i,0,temp.len-1)
{
temp.num[i]+=num[i]+x.num[i];
temp.num[i+1]=temp.num[i]/10;
temp.num[i]%=10;
}
while (temp.num[temp.len-1]==0 && temp.len>1)--temp.len;
return temp;
}
BIGNUM operator*(const BIGNUM &x)const
{
BIGNUM temp;
temp.len=len+x.len;
fo(i,0,len-1)
{
fo(j,0,x.len-1)
{
temp.num[i+j]+=num[i]*x.num[j];
temp.num[i+j+1]+=temp.num[i+j]/10;
temp.num[i+j]%=10;
}
}
while (temp.num[temp.len-1]==0 && temp.len>1)--temp.len;
return temp;
}
bool operator<(const BIGNUM &x)const
{
if (len>x.len)return 0;
if (len<x.len)return 1;
fd(i,len-1,0)if (num[i]!=x.num[i])return num[i]<x.num[i];
return 0;
}
void print()
{
fd(i,len-1,0)printf("%d",num[i]);
}
}f[MAXN][MAXN],pow,ans;
O3 int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n=read(),m=read(),pow=2;
fo(k,1,n)
{
memset(f,0,sizeof(f));
fo(i,1,m)a[i]=read(),f[i][i]=a[i]<<1;
fo(x,2,m)
{
fo(i,1,m)
{
ll j=i+x-1;
if (j>m)continue;
f[i][j]=max(f[i+1][j]*pow+f[i][i],f[i][j-1]*pow+f[j][j]);
}
}
ans=ans+f[1][m];
}
ans.print();
printf("\n");
return 0;
}