题解 - 矩阵取数游戏(线性-坐标dp)
今天学习了一下坐标dp,看到了下面个题:
这不so easy
吗?用个线性dp就能解决。
但是看到下面这个题之后,我就......
嗯......我还是太天真
了。
下面转入正题:
坐标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上有同样的题)
这个题目的主要意思其实就是给你一个矩阵,每一次取走每行的边缘值,问如何取才能使总和最大。
(以下内容部分摘自网络)
求最大的和,按照每一行取数,并不会影响到其他行,因此我们可以逐行dp,得到最大值。这样,我们就把坐标dp化繁为简,转化为类似线性dp的解法。
DP流程(每次DP仅针对第T行)
状态
我们用表示区间变为[i,j]
时,获得的最大分数。
转移
当区间变为[i,j]
时,上一次取数的时候区间一定是[i-1,j]
或[i,j+1]
,从这两个状态转移即可。在第m-j+i-1次取走了或
即:
终值
题目中说要取完,但是空区间是DP不出来的,然后就得手动模拟每个长度为11的区间。即:
上代码:
#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;
}
就到这里吧。
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/jzqsyx.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!