AcWing 1015. 摘花生
分析
code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int f[N][N], w[N][N];
int n, m;
int main(){
int T;
cin >> T;
while (T -- ){
memset(f, 0, sizeof f);
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> w[i][j];
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
f[i][j] += max(f[i - 1][j], f[i][j - 1]) + w[i][j];
cout << f[n][m] << endl;
}
return 0;
}
AcWing 1018. 最低通行费
分析
必须在2N- 1的的时间内穿越出去, 意味着只能往右边和往下走
code
#include <iostream>
using namespace std;
const int N = 110;
int w[N][N], f[N][N];
int n;
int main(){
cin >> n;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
cin >> w[i][j];
for (int i = 1 ; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (i == 1 && j == 1) f[i][j] = w[i][j];
else if (i == 1) f[i][j] = f[i][j - 1] + w[i][j];
else if (j == 1) f[i][j] = f[i - 1][j] + w[i][j];
else f[i][j] = min(f[i - 1][j], f[i][j - 1]) + w[i][j];
cout << f[n][n] << endl;
return 0;
}
AcWing 1027. 方格取数
分析
参考QinLaoDeMaChu的博客
本题的不同之处在于总共走两次。走两次可以是先后走两次,可以是同时并行的走两条路径,我们的方法是并行走两条。
走两次:
f[i1,j1,i2,j2] 表示所有从(1, 1), (1, 1) 分别走到 (i1, j1), (i2, j2) 的路径的最大值。
如何处理 “同一个格子不能重复选取”?
只有在 i1+j1 = i2+j2 时,两条路径的格子才可能重合。
有了这个等价关系,状态表示就可以优化为三维,
k 表示两条路径当前走到的格子横纵坐标之和, k = i1+j1 = i2 + j2 。
f[k, i1, i2] 表示 所有从(1, 1), (1, 1) 分别走到 (i1, k-i1), (i2, k-i2) 的路径的最大值。
集合如何划分呢?
我们只考虑最后一步,每条路径都有向下,向右两种选择,两条路径共有四种情况。
code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 11;
int w[N][N], f[2* N][N][N];
int n;
int main(){
cin >> n;
int x, y, c;
while (scanf("%d%d%d", &x, &y, &c), x || y || c){
w[x][y] = c;
}
for (int k = 2; k <= 2 * n; k ++ )
for (int i1 = 1; i1 <= n; i1 ++ )
for (int i2 = 1; i2 <= n; i2 ++ ){
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n){
int t = w[i1][j1];
if (i1 != i2) t += w[i2][j2];
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2 - 1] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2 - 1] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2] + t);
}
}
cout << f[2 * n][n][n] << endl;
return 0;
}
AcWing 275. 传纸条
分析
同上题, 此题做多走n + m
步
code(仿照上题)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 55;
int w[N][N], f[2* N][N][N];
int n, m;
void print(){
for (int k = 2; k <= n + m; k ++ )
for (int i1 = 1; i1 <= n; i1 ++ )
for (int i2 = 1; i2 <= n; i2 ++ )
printf("f[%d][%d][%d] = %d \n", k, i1, i2, f[k][i1][i2]);
}
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
cin >> w[i][j];
for (int k = 2; k <= n + m; k ++ )
for (int i1 = 1; i1 <= n; i1 ++ )
for (int i2 = 1; i2 <= n; i2 ++ ){
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= m && j2 >= 1 && j2 <= m){
int t = w[i1][j1];
if (i1 != i2) t += w[i2][j2];
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2 - 1] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - 1][i2] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2 - 1] + t);
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1][i2] + t);
}
}
// print();
cout << f[n + m][n][n] << endl;
return 0;
}
code(代码中间部分做优化)
根据
1
≤
j
1
=
k
−
i
1
≤
m
,
1
≤
j
2
=
k
−
i
2
≤
m
1 \leq j1 = k - i1 \leq m,\ 1\leq j2 = k - i2 \leq m
1≤j1=k−i1≤m, 1≤j2=k−i2≤m, 可以求得i1, i2
的边界
代码中间的循环, 做了优化
#include <iostream>
using namespace std;
const int N = 60;
int n, m;
int g[N][N];
int f[N * 2][N][N];
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
scanf("%d", &g[i][j]);
for(int k = 2; k <= n + m; k ++ )
for(int i1 = max(1, k - m); i1 <= min(k - 1, n); i1 ++ )
for(int i2 = max(1, k - m); i2 <= min(k - 1, n); i2 ++ ){
int t = g[i1][k - i1];
if (i1 != i2) t += g[i2][k - i2];
for(int a = 0; a <= 1; a ++ )
for(int b = 0; b <= 1; b ++ )
{
f[k][i1][i2] = max(f[k][i1][i2], f[k - 1][i1 - a][i2 - b] + t);
}
}
printf("%d\n", f[n + m][n][n]);
return 0;
}