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

本文介绍了一种针对矩阵取数游戏的策略算法,通过动态规划实现,旨在找到每次取数的最佳路径,以达到游戏结束时的最大得分。文章详细解析了算法思路,包括内外双向动态规划、高精度计算及__int128数据类型的应用,最终提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

description

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

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

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

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

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


analysis

  • 正解DP+DP+DP+高精度

  • 首先考虑DPDPDP,每行答案互不影响,所以可以对每行单独DPDPDP,把每行的答案加起来即可

  • 这里可以从外到内DPDPDP,也可以从内到外DPDPDP

  • 如果是从内到外DPDPDP的话,越往后取数数字越大,所以每次都是原来数字之和×2+×2+×2+现取数,不用预处理222的幂

  • 可以容易得到60pts60pts60pts的转移方程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])f[i][j]=max(f[i+1][j]2+f[i][i],f[i][j1]2+f[j][j])

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

  • 其实这题还有个tricktricktrick,答案的范围不会超过__int128int128int128,可以直接用这个,时空完爆高精度


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")

#include<stdio.h>
#include<string.h>
#include<algorithm>
#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<stdio.h>
#include<string.h>
#include<algorithm>
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值