返回上一页

题解 - 矩阵取数游戏(线性-坐标dp)

今天学习了一下坐标dp,看到了下面个题:
image
这不so easy吗?用个线性dp就能解决。
但是看到下面这个题之后,我就......
image
嗯......我还是太天真了。

下面转入正题:

坐标dp,实际上是线性dp的的、一个分支,就是把线性dp二维化的实现形式。

对于线性dp,大家应该不是很陌生了,如果还不会线性dp的可以在网上先学习线性dp再来学习。
坐标dp的一般实现形式:

for (int k = 3; k <= m + n; k++)
{
	for (int i = m; i; i--)
	{
		for (int j = m; j > i; j--)
		{
			f[i][j] = max(f[i][j], max(f[i - 1][j], max(f[i][j - 1], f[i - 1][j - 1])));
			f[i][j] += a[i][k-i] + a[j][k-j];
		}
	}
}

可以看到,坐标dp实际上就是把线性dp化一维为二维,但是这并不是定式,因此还是要具体问题具体分析。。

下面来看一看这个超级<恶心>的矩阵取数怎么做吧。(题目见下,Luogu P1005上有同样的题)

image

这个题目的主要意思其实就是给你一个矩阵,每一次取走每行的边缘值,问如何取才能使总和最大。

(以下内容部分摘自网络)
求最大的和,按照每一行取数,并不会影响到其他行,因此我们可以逐行dp,得到最大值。这样,我们就把坐标dp化繁为简,转化为类似线性dp的解法。

DP流程(每次DP仅针对第T行)
状态

我们用fi,j表示区间变为[i,j]时,获得的最大分数。

转移

当区间变为[i,j]时,上一次取数的时候区间一定是[i-1,j][i,j+1],从这两个状态转移即可。在第m-j+i-1次取走了Ai1,jAi,j+1
即:fi,j=max(fi1,j+Ai1,j2mj+i1,fi,j+1+Ai,j+12mj+i1)

终值

题目中说要取完,但是空区间是DP不出来的,然后就得手动模拟每个长度为11的区间。即:
Ans=max{fi,i+Ai,i2m}(im)

上代码:

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int a[1001], size;        //题目中数据范围较大,应使用高精
}f[101][101],mi[101],ans;     //下面用的是重载运算符,这样计算更方便
int m,juzhen[1001];
node operator+(const node &a, const node &b)
{   //高精加
	node c = { {0},0 };
	int al, bl, l;
	al = a.size;
	bl = b.size;
	l = max(al, bl);
	for (int i = 1; i <= l; i++)
	{
		c.a[i] += (a.a[i] + b.a[i]);
		c.a[i + 1] = c.a[i] / 10;
		c.a[i] %= 10;
	}
	if (c.a[l+1] != 0) c.size = l + 1;
	else c.size = l;
	return c;
}
node operator*(const node &a,const int &b)
{    //高精乘低精,这里我把低精转为了高精计算,其实可以不用转
	node c = { {0},0 }, b1 = { { 0 },0 }; int bb = b, bs = 0;
	int as = a.size;
	if ((as == 1 && a.a[1] == 0) || (b==0))
	{
		c.size = 1; c.a[1] = 0;
		return c;
	}
	while (bb) b1.a[++bs] = bb % 10, bb /= 10;
	int s = as + bs;
	for (int i = 0; i < bs; i++)
	{
		for (int j = 0; j < as; j++)
		{
			c.a[i + j+1] += a.a[j+1] * b1.a[i+1];
			c.a[i + j+2] += c.a[i + j+1] / 10;
			c.a[i + j+1] %= 10;
		}
	}
	while (!c.a[s]) s--;
	c.size = s;
	return c;
}
void print(node a)
{     //输出高精数
	for (int i = a.size; i; i--) cout << a.a[i];
}
void init()
{      //预处理2的n次方(需要高精)
	mi[0].size = 1; mi[0].a[1] = 1;
	for (int i = 1; i <= m+3; i++) mi[i] = mi[i - 1] * 2;// , cout << i << ' ', print(mi[i]), cout << endl;
}
node max(node a, node b)
{       //高精取最大值
	if (a.size > b.size) return a;
	if (a.size < b.size) return b;
	for (int i = a.size; i; i--)
	{
		if (a.a[i] > b.a[i]) return a;
		if (a.a[i] < b.a[i]) return b;
	}
	return a;
}
int main()
{
	cin >> n >> m;
	init();
	for (int _ = 1; _ <= n; _++)
	{
		memset(f, 0, sizeof(f));  //不能省略!否则会影响下面矩阵值的计算
		for (int i = 1; i <= m; i++) cin >> juzhen[i];
		for (int i = 1; i <= m; i++)
		{
			for (int j = m; j >= i; j--)   //动态规划核心
			{
				f[i][j] = max(f[i][j], max(f[i - 1][j] + mi[m - j + i - 1] * juzhen[i - 1], f[i][j + 1] + mi[m - j + i - 1] * juzhen[j + 1]));
			}
		}
		node _max = { {0} ,0 };
		for (int i = 1; i <= m; i++) _max = max(_max, f[i][i] + mi[m] * juzhen[i]);
		ans = ans + _max;  //把每行结果加和
	}
	print(ans);
	return 0;
}

就到这里吧。

posted @   1Liu  阅读(240)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示