动态规划入门
例题(题目来源:洛谷)
1.P1002 过河卒
2.P1359 租用游艇
1
题目:棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。 棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。 现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。 现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
思路:
第一眼看到这个题感觉暴搜应该也能过,记忆化搜索也行,dp也可以,但博主正在练习入门dp,所以就用了dp。
由原点向下推,比如到达第二个点有2条,到达第3个点有1条,第四个点又只能从第二个和第三个到达,那么到达第四个点有几条路线呢?显然就是2+1条。那么递推下去求到最后一个点,就可以得到所有的路线。
我们可以知道卒的行动路线,即向右或向下,因此我们能够很直观的看出来每个点可以由前面哪些到达。举个例子,坐标为(2,2)的点由(2,1)和(1,2)到达,因此可以写出动态方程dp[i][j] = max(dp[i-1][j] + dp[i][j-1], dp[i][j]);
这个题有个边界限制,不可以超出棋盘,我们可以这样做:在读如的时候拓宽棋盘边界,即多出来一圈,但多出来的也会被加上,但是如果是+1的话那么就不影响了。
下面是代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 500;
long long dp[maxn][maxn]; //dp[i][j]表示到达(i,j)点的路径 要开long long不然可能WA
bool cannot[maxn][maxn];//马可能到的点
int n, m, horse_x, horse_y;
int dx[] = {0, -1, 1, -1, 1, -2, 2, -2, 2};
int dy[] = {0, -2, -2, 2, 2, -1, -1, 1, 1};
int main() {
cin >> n >> m >> horse_x >> horse_y;
n += 1, m += 1, horse_x += 1, horse_y += 1; //+1防止越界
for (int i = 0; i < 9; i++) {
int xx = horse_x + dx[i], yy = horse_y + dy[i];
if ((xx >= 0 && xx <= n) && (yy >= 0 && yy <= m)) {
cannot[xx][yy] = 1;
}
}
dp[1][1] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (cannot[i][j]) {
continue;
} else {
dp[i][j] = max(dp[i-1][j] + dp[i][j-1], dp[i][j]);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
一个类似的练习 P1130 红牌
2
题目:长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,…,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站i 到游艇出租站j 之间的租金为r(i,j),1<=i<=j<=n。试设计一个算法,计算出从游艇出租站1 到游艇出租站n 所需的最少租金。 对于给定的游艇出租站i 到游艇出租站j 之间的租金为r(i,j),1<=i<j<=n,编程计算从游艇出租站1 到游艇出租站n所需的最少租金。 保证计算过程中任何时刻数值都不超过10^6
思路:
先读入矩阵
接下来是动态方程,dp[i]表示从1到i所需要的最小值
问题就在于我们如何判断最小值,我们应该用什么判断呢?
因为从1出发,所以dp[1]=0,不做讨论,从前往后推,即从第二个开始
每个的最小值等于多少呢?显然一个循环是不足以让我们求出最小值,
我们需要另一个循环,即从1开始遍历到i-1。
以4为例
第一次:dp[4] = min(dp[4], dp[1]+a[1][4]);
dp[4] 更新一次
第二次:dp[4] = min(dp[4], dp[2]+a[2][4]);
第一次是从这三个中选一个更小的。
…
第i-1次:dp[i] = min(dp[i], dp[i-1]+a[i-1][i]);
就是之前所有情况都已经考虑了一遍,筛选出了最小值。
下面是代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e3;
int n;
int a[maxn][maxn], dp[maxn];
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
for (int j = i+1; j <= n; j++) {
cin >> a[i][j];
}
}
for (int i = 2; i <= n; i++) {
dp[i] = a[1][i];
for (int j = 1; j < i; j++) {
dp[i] = min(dp[i], dp[j]+a[j][i]);
}
}
cout << dp[n];
return 0;
}
欢迎各位看的大佬指正。
上一篇: windows下环境安装Jmeter
下一篇: 涂抹果酱(三进制状压dp)