传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1910
这就是noi2013 penman 的简化版嘛,只有一个I的话,dp就简单很多了,我们发现I是由3个小矩形组成的,所以我们一行一行dp,
枚举它属于哪一个矩形,左边界和右边界就行了,至于矩形间的转移,再用一个g数组保存上一个矩形的最大值就行了。
第一次做图形dp,感觉就是要把握住图形的特点,再充分利用各种条件优化就行了。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define X(x) ((x) > 0 ? (x) : 0) const int maxn = 210; int f[3][2][maxn][maxn], g[2][maxn][maxn]; int a[maxn][maxn]; int n, m, ans; void update(int &x, int y, int z) { if(y == -1) return; x = max(x, y + z); return; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { scanf("%d", &a[i][j]); } } memset(f, -1, sizeof(f)); memset(g, -1, sizeof(g)); memset(f[0][0], 0, sizeof(f[0][0])); for(int x = 1; x <= n; x ++) { for(int i = 0; i < 3; i ++) memset(f[i][x & 1], -1, sizeof(f[i][x & 1])); for(int l = 1; l <= m; l ++) { if(a[x][l]) continue; for(int r = l; r <= m; r ++) { if(a[x][r]) break; update(f[0][x & 1][l][r], X(f[0][(x - 1) & 1][l][r]), (r - l + 1)); update(f[1][x & 1][l][r], f[1][(x - 1) & 1][l][r], (r - l + 1)); if(l > 1 && r < m) update(f[1][x & 1][l][r], g[0][l - 1][r + 1], (r - l + 1)); update(f[2][x & 1][l][r], f[2][(x - 1) & 1][l][r], (r - l + 1)); if(l + 1 < r) update(f[2][x & 1][l][r], g[1][l + 1][r - 1], (r - l + 1)); update(ans, f[2][x & 1][l][r], 0); } } memset(g, -1, sizeof(g)); for(int len = m; len ; len --) { for(int l = 1; l + len - 1 <= m; l ++) { int r = l + len - 1, t = -1; if(l > 1) t = max(t, g[0][l - 1][r]); if(r < m) t = max(t, g[0][l][r + 1]); g[0][l][r] = max(f[0][x & 1][l][r], t); } } for(int len = 1; len <= m; len ++) { for(int l = 1; l + len - 1 <= m; l ++) { int r = l + len - 1, t = -1; if(l < r) t = max(t, g[1][l + 1][r]); if(l < r) t = max(t, g[1][l][r - 1]); g[1][l][r] = max(f[1][x & 1][l][r], t); } } } printf("%d\n", ans); return 0; }