【JZOJ1181】【luoguP1005】【NOIP2007】矩阵取数游戏

description

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素a[i,j] 均为非负整数。游戏规则如下:

每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;

每次取走的各个元素只能是该元素所在行的行首或行尾;

每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);

游戏结束总得分为m次取数得分之和。帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。


analysis

  • 正解 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][j1]2+f[j][j])

  • 这个方程再套上一个高精度的板子即可 A C AC AC

  • 其实这题还有个 t r i c k trick trick,答案的范围不会超过__ i n t 128 int128 int128,可以直接用这个,时空完爆高精度


code

#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;
}

你可能感兴趣的:(DP,模拟赛,高精度,模拟赛,高精度,DP)