# 经典dp问题 矩阵取数以及变形

dp[i][j] = dp[i-1][j] + dp[i][j-1]; O(n^2)的复杂度.
AC Code

const int maxn = 5e2+5;
int a[maxn][maxn], dp[maxn][maxn];
void solve()
{
int n;
cin >> n;
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= n ; j ++) {
scanf("%d", &a[i][j]);
}
}
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= n ; j ++) {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + a[i][j];
}
}
cout << dp[n][n] << endl;
}

AC Code

const int maxn = 50+5;
int a[maxn][maxn];
ll dp[maxn][maxn];
int dx[] = {-1, -1, -2, -2, 1, 1, 2, 2};
int dy[] = {2, -2, 1, -1, 2, -2, 1, -1};
void solve()
{
int ex, ey, rx, ry;
cin >> ex >> ey >> rx >> ry;
for (int i = 0 ; i < 8 ; i ++) {
int xx = rx + dx[i];
int yy = ry + dy[i];
if (xx < 0 || yy < 0) continue;
a[xx][yy] = -1;
}
a[rx][ry] = -1;
dp[0][0] = 1;
for (int i = 0 ; i <= 20 ; i ++) {
for (int j = 0 ; j <= 20 ; j ++) {
if (a[i][j] == -1) continue;
if (i) dp[i][j] = dp[i-1][j];
if (j) dp[i][j] += dp[i][j-1];
}
if (dp[ex][ey]) break;
}
printf("%lld\n", dp[ex][ey]);
}

AC Code

const int maxn = 1e2+5;
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
struct node {
int x, y, w;
bool operator <( const node& _) const {
return w < _.w;
}
}e[maxn*maxn];
int a[maxn][maxn], dp[maxn][maxn];
void solve()
{
string s; int n, m;
while(cin >> s >> n >> m) {
int k = 0; Fill(a, -1); Fill(dp, 0);
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
int x; scanf("%d", &x);
e[++k] = node{i, j, x};
}
}
sort(e+1, e+1+k); int ans = 0;
for (int i = 1 ; i <= k ; i ++) {
int x = e[i].x, y = e[i].y;
a[x][y] = e[i].w; dp[x][y] = 1;
for (int j = 0 ; j < 4 ; j ++) {
int tmp = a[x+dx[j]][y+dy[j]];
int val = dp[x+dx[j]][y+dy[j]] + 1;
if (tmp != -1 && tmp < e[i].w && val > dp[x][y]) {
dp[x][y] = val;
ans = max(ans, dp[x][y]);
}
}
}
cout << s << ": " << ans << endl;
}
}

(这道题非常经典, 一定要记住这道题!)

AC Code

const int inf = 0x3f3f3f3f; //用这个可以直接mem
const ll INF = 1e18;
const int mod = 1e9+7;
const int maxn = 1e2+5;
int a[15][maxn], pre[15][maxn], dp[15][maxn];
int Next[15][maxn];
int path[maxn];
void solve()
{
int n, m;
while(~scanf("%d%d", &n, &m)) {
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
scanf("%d", &a[i][j]);
}
}
int st = 1;
Fill(dp, inf); int ans = inf;
for (int i = m ; i >= 1 ; i --) { // 倒着推, 方便记录路径
for (int j = 1 ; j <= n ; j ++) {
if (i == m) dp[j][i] = a[j][i]; // 更新初始状态
else {
int r[3] = {j-1, j, j+1};
if (j == 1) r[0] = n; // 处理边界问题
if (j == n) r[2] = 1;
sort(r, r+3); // 这样就可以保证字典序最小啦.
for (int k = 0 ; k < 3 ; k ++) {
if (dp[r[k]][i+1] + a[j][i] < dp[j][i]) {
dp[j][i] = dp[r[k]][i+1] + a[j][i];
Next[j][i] = r[k];
}
}
}
if (i == 1 && dp[j][i] < ans) ans = dp[j][i], st = j;
// 同样我们行是从小枚举的, 这样也就可以保证字典序乐.
}
}
printf("%d", st);
for (int i = 1 ; i < m ; i ++) {
printf(" %d", Next[st][i]);
st = Next[st][i];
} // 打印路径即可
printf("\n%d\n", ans);
}
}

AC Code

const int inf = 0x3f3f3f3f; //用这个可以直接mem
const ll INF = 1e18;
const int mod = 1e9+7;
const int maxn = 100+5;
int a[maxn][maxn];
int dp[maxn][maxn], Next[maxn][maxn];
void solve()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
scanf("%d", &a[i][j]);
}
}
int st = 1, mi = inf; Fill(dp, inf);
for (int i = n ; i >= 1 ; i --) { // 因为要打印路径, 所以还是倒着推.
for (int j = m ; j >= 1 ; j --) { // 倒着可以保证起始点字典序最大.
if (i == n) dp[i][j] = a[i][j];
else { // 为了字典序最大就这样更新即可.
if (j + 1 <= m && dp[i+1][j+1]  + a[i][j] < dp[i][j]) {
dp[i][j] = dp[i+1][j+1]  + a[i][j];
Next[i][j] = j+1;
}
if (dp[i+1][j]  + a[i][j] < dp[i][j]) {
dp[i][j] = dp[i+1][j]  + a[i][j];
Next[i][j] = j;
}
if (j - 1 >= 1 && dp[i+1][j-1]  + a[i][j] < dp[i][j]) {
dp[i][j] = dp[i+1][j-1]  + a[i][j];
Next[i][j] = j-1;
}
}
if (i == 1 && dp[i][j] < mi) mi = dp[i][j], st = j;
}
}
printf("%d", st);
for (int i = 1 ; i < n ; i ++) {
st = Next[i][st];
printf(" %d", st);
}