区间DP矩阵取数游戏

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间DP矩阵取数游戏相关的知识,希望对你有一定的参考价值。

  • 博客主页: https://blog.csdn.net/qq_50285142
  • 欢迎点赞👍收藏✨关注❤留言 📝 如有错误,敬请指正
  • 🎈虽然生活很难,但我们也要一直走下去🎈

原题链接

题意:
对于一个给定的n×m 的矩阵,矩阵中的每个元素 均为非负整数
游戏规则如下:
每次取数时须从每行各取走一个元素,共 n 个。经过 m 次后取完矩阵内所有元素;
每次取走的各个元素只能是该元素所在行的行首或行尾;
每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 × 2 i 2^i 2i, 其中 i 表示第 i 次取数(从 1 开始编号),游戏结束总得分为 m 次取数得分之和。
对于任意矩阵,可以求出取数后的最大得分。

思路:
可以发现每行之间的结果互不影响,所以我们只需要弄明白怎么求一行的最大值即可
求一行区间的最大值,自然想到区间DP
区间DP是从小区间更新到大区间,我们可以考虑状态表示
状态表示:
f [ i ] [ j ] f[i][j] f[i][j]表示区间 [ i , j ] [ i , j ] [i,j]中取数得到的最大值,这是取数的次数自然是 j − i + 1 j-i+1 ji+1
状态转移:
一个区间一般都是由两个状态转移过来,一个是前面少一个元素,一个是后面少一个元素。即
f [ i ] [ j ] = m a x ( f [ i + 1 ] [ j ] + a [ k ] [ i ] ∗ 2 m − j + i , f [ i ] [ j − 1 ] + a [ k ] [ j ] ∗ 2 m − j + i ) f[i][j] = max(f[i+1][j]+a[k][i]*2^{m-j+i},f[i][j-1]+a[k][j]*2^{m-j+i}) f[i][j]=max(f[i+1][j]+a[k][i]2mj+i,f[i][j1]+a[k][j]2mj+i)
其中少的那一个元素为第 m − j + i m-j+i mj+i次取数,所以为相应的值乘2的对应幂次,一直从小的区间转移,直到转移到大区间
但是区间DP我喜欢用len长度的写法,所以相对应的转移方程就要变一下,其实本质不变
f [ l ] [ l + l e n − 1 ] = m a x ( f [ l + 1 ] [ l + l e n − 1 ] + a [ k ] [ l ] ∗ f a c t [ m − l e n + 1 ] , f [ l ] [ l + l e n − 2 ] + a [ k ] [ l + l e n − 1 ] ∗ f a c t [ m − l e n + 1 ] ) f[l][l+len-1] = max(f[l+1][l+len-1]+a[k][l]*fact[m-len+1], f[l][l+len-2]+a[k][l+len-1]*fact[m-len+1]) f[l][l+len1]=max(f[l+1][l+len1]+a[k][l]fact[mlen+1],f[l][l+len2]+a[k][l+len1]fact[mlen+1])

本题需要注意的是:
题目中数组中存储的数据超出了64位,但是发现int128可以满足数据范围的要求,所以就使用int128比较简便,int128int差不多,就是读入输出需要写一个对应的函数,其他的使用都一样

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+100;

__int128 f[1005][1005],fact[100],res;
int n,m,a[1005][1005];

inline __int128 read()
{
	__int128 x = 0;
	int f = 1;
	char ch;
	ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')
		{
			f = -1;
			ch = getchar();
		}
	}
	while(ch>='0' and ch<='9') x = x*10+ch-'0',ch=getchar();
	return x*f;
}

void print(__int128 x)
{
	if(x<0) 
	{
		putchar('-');
		x = -x;
	}
	if(x>9) print(x/10);
	putchar(x % 10+'0');
}  
 
void solve()
{
	fact[0] = 1;
	for(int i=1;i<=80;i++)
		fact[i] = fact[i-1] * 2;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=m;i++) f[i][i] = a[k][i] * fact[m];
		
		for(int len=2;len<=m;len++)
		{
			for(int l=1;l+len-1<=m;l++)
			{
				f[l][l+len-1] = max(f[l+1][l+len-1]+a[k][l]*fact[m-len+1],
				f[l][l+len-2]+a[k][l+len-1]*fact[m-len+1]);
			}
		}
		res += f[1][m];
	}
	print(res);
}
int main()
{
	int _;
//	cin>>_;
	_ = 1;
	while(_--)
	{
		solve();
	}
	return 0;
}

往期优质文章推荐

以上是关于区间DP矩阵取数游戏的主要内容,如果未能解决你的问题,请参考以下文章

区间DP矩阵取数游戏

luogu P1005 矩阵取数游戏 区间DP

codevs 1166 矩阵取数游戏

[DP 区间 高精]矩阵取数

dp算法第二发之noip矩阵取数游戏

[luoguP1005] 矩阵取数游戏(DP + 高精度)