所谓棋盘dp就是dp[i][j]代表了(1,1)到(i,j)范围内的收益,在矩阵前缀时,利用了一些容斥原理达到状态转移的目的。本题为了维护矩阵形状,考虑到每个点其实只能一直向上或者一直向下,只需要dp[i-1][j]加上左边前缀和与dp[i][j-1]加上上部前缀和即可。
唯一让我疑惑的是,本次i,j的连接方式和其内部的1连接方式根本不同时,为什么不会产生影响呢?
宏观来说,我们向左插入直线时,选择的是上部的矩形,我们向上插入时,选择的是左部的,直线与被选择的矩形根本不想交,所以直线与矩形根本不会互相影响。而我们dp[i-1][j]与d[i][j-1]的状态内部已经安排好了没有冲突的最优情况。我们此时加入一条左或上的边,也是合理的,那么dp[i][j]就合理了,其他更大的矩形就可以利用这一合理性,构建更大的合理。
微观来说,见图。
首先看边界,最上面一定全部向左时,dp[1][j]才能最大值,最左侧全部向上时dp[i][1]才能取最大值。由于(1,1)左右答案都相同,所以这还不能充分说明问题。
到达(1,2)时,两种扩展方式,向上时,dp[i][j-1]内部不会影响本次操作。但本次方向显然和上方(1,2)位置方向发生冲突,但暂时还没有影响后续结果,我们继续推,假设本次是向上推最优。
(2,3)位置,向右推时,dp[2][2]并不会影响本次操作,而且我们这样也把(1,3)改成了向上,不会存在断层情况。那如果本次向左扩展最优呢,那么我们又得把左边全部染成黄色,dp[1][3]派上了用场,dp[1][3]在到达(2,2)点时被“修改”了部分方向,可我们调用时,它仍然保持这它内部最优的情况,而这种情况是连续无断层的。
#include <iostream>
# include<algorithm>
# include<cstring>
using namespace std;
typedef long long int ll;
int dp[550][550];
int bei[550][550];
int xi[550][550];
int main()
{
int n,m;
while(cin>>n>>m&&n&&m)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
cin>>x;
xi[i][j]=xi[i][j-1]+x;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
cin>>x;
bei[i][j]=bei[i-1][j]+x;
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j]=max(dp[i-1][j]+xi[i][j],dp[i][j-1]+bei[i][j]);
}
}
cout<<dp[n][m]<<'\n';
}
return 0;
}
以此类推,发现即使出现了内部冲突的情况,也不会对本次造成任何影响。