|
|
|
@ -0,0 +1,94 @@
|
|
|
|
|
## 数字三角形 【干货、背诵版】
|
|
|
|
|
|
|
|
|
|
#### 基础题
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1015$. 摘花生](https://www.acwing.com/problem/content/1017/)**
|
|
|
|
|
|
|
|
|
|
二维状态表示,双重循环填充二维表,从上或右可以转移
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
for (int i = 1; i <= n; i++)
|
|
|
|
|
for (int j = 1; j <= m; j++)
|
|
|
|
|
// 递推的出发点,采用特判的办法手动维护,其它的靠关系式递推完成
|
|
|
|
|
// 之所以这样对起点进行初始化,是因为只有这样才能保障逻辑自洽
|
|
|
|
|
if (i == 1 && j == 1)
|
|
|
|
|
f[i][j] = w[i][j];
|
|
|
|
|
else
|
|
|
|
|
f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
初始值
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
f[1][1] = w[1][1]; //出发点
|
|
|
|
|
```
|
|
|
|
|
① **状态表示**
|
|
|
|
|
$f[i][j]$代表走到第$(i,j)$个位置时可以获取到的最大数量和
|
|
|
|
|
|
|
|
|
|
② **状态转移**
|
|
|
|
|
即$f[i][j]$可以从哪些状态转移而来
|
|
|
|
|
|
|
|
|
|
③ **初始值**
|
|
|
|
|
思考起点下状态表示的现实含义即可
|
|
|
|
|
|
|
|
|
|
**总结**
|
|
|
|
|
① 动态规划需要按上面的三步走,而最难的是状态表示,状态转移还好,根据题意可以推出相应的转换关系。状态表示一般是通过 **经验**,对,是 **经验**。
|
|
|
|
|
|
|
|
|
|
② 初始值:不一定非得放在循环外进行初始化,也可以合并在循环内,有时放在循环内初始化反而更方便更直接。
|
|
|
|
|
|
|
|
|
|
③ 可以使用一维表示,但我个人理解不如二维的直观,最起码思路应该是先有两维再缩成一维,而不是上来就直接一维。一般的题目不会对内存有严格的限制,能用二维还是二维吧,实在卡内存再考虑降为一维。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1018$. 最低通行费](https://www.acwing.com/problem/content/1020/)**
|
|
|
|
|
与上一题基本一样,只不过是变换了下,需要你用思维转一下:$2n-1$次,就是说只能向下或向右,否则次数就不达标了。还有一点,就是需要求最小值,而不是最大值,其它的没区别
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
//初始化为最大值
|
|
|
|
|
memset(f, 0x3f, sizeof f);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
f[i][j] = min(f[i - 1][j], f[i][j - 1]) + w[i][j];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 进阶题
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1027$. 方格取数](https://www.acwing.com/problem/content/1029/)**
|
|
|
|
|
走两次,其实是故意让我们真的走两回,每次取最优。
|
|
|
|
|
|
|
|
|
|
很明显,这是坑。因为如果每次走时不考虑别人,只考虑自己,就是局部最优,不是全局最优,这样就是贪心,不是动态规划。
|
|
|
|
|
|
|
|
|
|
解决办法就是把两次一起考虑,转化为有两个小人一起走,步调一致,此时两个相亲相爱,互相照顾,白头偕老,皆大欢喜:
|
|
|
|
|
|
|
|
|
|
状态表示也就呼之欲出:
|
|
|
|
|
$f[x_1][y_1][x_2][y_2]$表示第1个人走到$(x_1,y_1)$,第2个人走到$(x_2,y_2)$,但需要保证$x_1+y_1=x_2+y_2$
|
|
|
|
|
|
|
|
|
|
**状态转移**:
|
|
|
|
|
① 如果两个人走到同一个位置,就可能加上一次这个位置上的数
|
|
|
|
|
② 如果两个人走到不同的位置,就可以加上两个地方上的数
|
|
|
|
|
③ 两个坐标共同决定了现在的状态,换句话说,状态和上面的两个坐标都是相关的
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
//开始递推
|
|
|
|
|
for (int x1 = 1; x1 <= n; x1++)
|
|
|
|
|
for (int y1 = 1; y1 <= n; y1++)
|
|
|
|
|
for (int x2 = 1; x2 <= n; x2++)
|
|
|
|
|
for (int y2 = 1; y2 <= n; y2++) {
|
|
|
|
|
if (x1 + y1 == x2 + y2) {
|
|
|
|
|
int t = f[x1 - 1][y1][x2 - 1][y2]; //下下
|
|
|
|
|
t = max(t, f[x1][y1 - 1][x2][y2 - 1]); //右右
|
|
|
|
|
t = max(t, f[x1 - 1][y1][x2][y2 - 1]); //下右
|
|
|
|
|
t = max(t, f[x1][y1 - 1][x2 - 1][y2]); //右下
|
|
|
|
|
//加上这个点的数值
|
|
|
|
|
f[x1][y1][x2][y2] = t + w[x1][y1];
|
|
|
|
|
//如果这个点没有被重复走,那么再加一次w(x2,y2)
|
|
|
|
|
if (x1 != x2 && y1 != y2) f[x1][y1][x2][y2] += w[x2][y2];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $275$. 传纸条](https://www.acwing.com/problem/content/277/)**
|
|
|
|
|
与上面的题目是一个题。
|