最大点独立集+DP
答案就是在图中选出一些点,使得两两不可达且权值和最大。
然而并没有找到证明,于是只好自己脑补。大概是会证(口)明(胡)了吧……现给出我的证明如下:
先分析出行进的策略:走到第一行最右的一个非零格子,向下走一行进入第二行,走到右边最右的一个非零格子(若无则不走),向下走一行进入第三行,走到右边最右的一个非零格子(若无则不走) ……走到右下角。
证明:易知选出的点集中最右上的(称作x)那个一定在第一遍的行进路上。只需证明当x的权值减完之后,点集S中除x以外的最右上的点(称作y)在接下来的行进路上,即可说明存在这样一种方案。
首先,显然x,y围成的矩形内(不含边界)中不可能有点,否则加入点集更优。假设y无法进入当前行进路,即y的右上角还有点,设为u。易知这个点不会在x的右上方,否则就不会轮到x。这个点只能在[y的右上]与[x的左上或右下] 的交集之中。此时用u替换x,点集S权值更优。但可能导致u和x的前一个点集中的右上的点无法衔接,即变得可达。设x的前一个点集中的右上点为z。如果z一直都在消u,则把z也丢掉即可。如果z消u之前在消v(即v,u不可达,v不属于点集S),则用v,u共同替换x,z答案更优,此时继续递归寻找z的右上点,变成子问题。点集S中最右上的点没有右上点,因此子问题可以结束,即可以找到最优解。
可能你看不懂我在说什么,建议把图画出来比一比,那样应该就知道了QAQ
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
using namespace std;
namespace runzhe2000
{
int f[N][N], a[N][N];
void main()
{
int T;
scanf("%d",&T);
for(; T--;)
{
int n, m;
scanf("%d%d",&n,&m); memset(f,0,sizeof f);
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d",&a[i][j]);
for(int i = 1; i <= n; i++) for(int j = m; j >= 1; j--) f[i][j] = max(a[i][j] + f[i-1][j+1], max(f[i-1][j], f[i][j+1]));
printf("%d\n",f[n][1]);
}
}
}
int main()
{
runzhe2000::main();
}