数字三角形
从搜索到规划
数字三角形
Luogu P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | #include <iostream>
#include <cstring>
using namespace std;
const int N=1e3+10;
int n;
int dp[N][N];
int main(){
memset(dp, -0x3f, sizeof dp);
cin>>n;
for(int i=1; i<=n; i++)
for(int j=1; j<=i; j++)
scanf("%d", &dp[i][j]);
for(int i=2; i<=n; i++)
for(int j=1; j<=i; j++)
dp[i][j]=max(dp[i-1][j-1], dp[i-1][j])+dp[i][j];
int ans=-0x3f3f3f3f;
for(int i=1; i<=n; i++)
ans=max(ans, dp[n][i]);
cout<<ans;
return 0;
}
|
从左上到右下
朴素
AcWing 1015. 摘花生 code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | #pragma G++ optimize("Ofast")
#include <iostream>
#include <cstring>
using namespace std;
const int N=110;
int dp[N][N];
int R, C;
void solve(){
cin>>R>>C;
for(int i=1; i<=R; i++)
for(int j=1; j<=C; j++)
cin>>dp[i][j];
for(int i=1; i<=R; i++)
for(int j=1; j<=C; j++)
dp[i][j]=max(dp[i-1][j], dp[i][j-1])+dp[i][j];
cout<<dp[R][C]<<"\n";
}
int main(){
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
int T; cin>>T; while(T--)
solve();
return 0;
}
|
存在障碍
Luogu P1002 [NOIP2002 普及组] 过河卒 code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | #define fst first
#define sed second
#include <iostream>
#include <set>
using namespace std;
typedef pair<int, int> PII;
const int N = 30;
long long dp[N][N]; // 到达点 i, j 的最短路径
set <PII> H;
int main() {
PII c, b; // b是马
cin >> c.fst >> c.sed >> b.fst >> b.sed;
b.fst++, b.sed++, c.fst++, c.sed++;
H.insert({ b.fst, b.sed });
H.insert({ b.fst - 1, b.sed - 2 });
H.insert({ b.fst - 2, b.sed - 1 });
H.insert({ b.fst - 1, b.sed + 2 });
H.insert({ b.fst - 2, b.sed + 1 });
H.insert({ b.fst + 1, b.sed - 2 });
H.insert({ b.fst + 2, b.sed - 1 });
H.insert({ b.fst + 1, b.sed + 2 });
H.insert({ b.fst + 2, b.sed + 1 });
dp[0][1] = 1;
for (int i = 1; i <= c.fst; i++)
for (int j = 1; j <= c.sed; j++)
if (H.count({ i, j }))
dp[i][j] = 0;
else
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
cout << dp[c.fst][c.sed];
return 0;
}
|
存在多种走法
Luogu P7074 [CSP-J2020] 方格取数 code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | #include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int N=1e3+10;
int g[N][N];
LL dp[N][N][2];
int n, m;
int main(){
//freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);
memset(g, -0x3f, sizeof g);
memset(dp, -0x3f, sizeof dp);
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
scanf("%d", &g[i][j]);
dp[1][1][0]=dp[1][1][1]=g[1][1];
for(int i=2; i<=n; i++) // 用手导第一列
dp[i][1][0] = dp[i][1][1] = dp[i-1][1][0] + g[i][1];
//for(int i=n-1; i>=1; i--)
// dp[i][1][1] = dp[i+1][1][1] + g[i][1];
//for(int i=1; i<=n; i++)
// dp[i][1][0] = dp[i][1][1] = max(dp[i][1][0], dp[i][1][1]);
for(int j=2; j<=m; j++){
for(int i=1; i<=n; i++) // 从上到下,从左边来,从上面来
dp[i][j][0] = max(dp[i][j-1][0], dp[i-1][j][0]) + g[i][j];
for(int i=n; i>=1; i--) // 从下到上,从左边来,从下面来
dp[i][j][1] = max(dp[i][j-1][1], dp[i+1][j][1]) + g[i][j];
for(int i=1; i<=n; i++)
dp[i][j][0] = dp[i][j][1] = max(dp[i][j][0], dp[i][j][1]);
}
cout<<dp[n][m][1];
return 0;
}
|
允许重复走
Luogu P1004 [NOIP2000 提高组] 方格取数
题目大意
- 嘻嘻
解题思路
-
如果分两次走,会因为第一次取走的数而影响第二次的取数,没有保证无后效性,因此考虑一次走完
-
显然,第二次走,也可以看作是左上角到右下角,因此可以理解为两次从左上角到右下角取数
-
用 \(dp[i][j][x][y]\) 表示第一次走到 \((i, j)\),第二次走到 \((x, y)\) 的最大值
-
如果 \((i, j) = (x, y)\),需要特判,因为只能取一次
code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | #include <iostream>
using namespace std;
const int N = 15;
int g[N][N];
int dp[N][N][N][N];
int n;
int main() {
cin >> n;
while (1) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if (a == b && a == c && a == 0)
break;
g[a][b] = c;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int x = 1; x <= n; x++)
for (int y = 1; y <= n; y++) {
int t = (i == x && j == y) ? g[i][y] : g[i][j] + g[x][y];
dp[i][j][x][y] = max(dp[i][j][x][y], dp[i - 1][j][x - 1][y] + t);
dp[i][j][x][y] = max(dp[i][j][x][y], dp[i - 1][j][x][y - 1] + t);
dp[i][j][x][y] = max(dp[i][j][x][y], dp[i][j - 1][x - 1][y] + t);
dp[i][j][x][y] = max(dp[i][j][x][y], dp[i][j - 1][x][y - 1] + t);
}
cout << dp[n][n][n][n];
return 0;
}
|
重复走的优化
Luogu P1006 [NOIP2008 提高组] 传纸条 code
C++ |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | #include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 60;
int g[N][N];
int dp[N*2][N][N];
// 第一维存总的步数
// 第二维存第一次走的纵向距离,那么横向距离就是k-x
// 第三维存第二次的,同理
int n,m;
int main(){
cin>>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 i=1;i<k&&i<=n;i++)
for(int j=1;j< k&&j<=n;j++){
int v = g[i][k-i];
if(i!=j) v+= g[j][k-j];
dp[k][i][j] = max(dp[k][i][j], v + dp[k-1][i][j]);
dp[k][i][j] = max(dp[k][i][j], v + dp[k-1][i][j-1]);
dp[k][i][j] = max(dp[k][i][j], v + dp[k-1][i-1][j]);
dp[k][i][j] = max(dp[k][i][j], v + dp[k-1][i-1][j-1]);
}
cout<<dp[n+m][n][n]<<endl;
return 0;
}
|