[NOIP2008]传纸条
一道人人皆知的dp基础题。我听人讲了无数遍,就是没写。
问题可以转化为找到两条从(1, 1)到(n, m)不相交的路径,使路径上的好感度之和最大。
这道题的关键在于dp的顺序,观察会发现,只要保持这两条路径枚举到同一条对角线上,顺序就对了。于是令dp[k][i][j]表示到第k条对角线,靠右的点的纵坐标为 i ,靠左点的纵坐标为 j 时的答案。每一个点有两种转移方式,所以一共有四种转移方式。枚举即可。
最后的状态应该是dp[n + m - 1][n - 1][n],而不是dp[n + m][n][n],因为要保证两条路径不相交,那么dp[n + m][n][n]显然是一个不合法状态,所以答案是这个状态的上一个状态。
另外可以倒着枚举优化掉第一维,但对于这一题来说没有必要。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 55; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) last = ch, ch = getchar(); 26 while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, m, a[maxn][maxn]; 38 ll dp[maxn << 1][maxn][maxn]; 39 40 int main() 41 { 42 n = read(); m = read(); 43 for(int i = 1; i <= n; ++i) 44 for(int j = 1; j <= m; ++j) a[i][j] = read(); 45 for(int k = 3; k < n + m; ++k) 46 for(int i = 1; i < k - 1; ++i) 47 for(int j = i + 1; j < k; ++j) 48 { 49 ll tp = 0; 50 tp = max(tp, dp[k - 1][i][j]); 51 tp = max(tp, dp[k - 1][i - 1][j]); 52 if(j - 1 != i) tp = max(tp, dp[k - 1][i][j - 1]); 53 tp = max(tp, dp[k - 1][i - 1][j - 1]); 54 dp[k][i][j] = tp + a[i][k - i] + a[j][k - j]; 55 } 56 write(dp[n + m - 1][n - 1][n]), enter; 57 return 0; 58 }