[DP] 三角蛋糕 题解
题目描述
XP在机房里放了一块正三角形的大蛋糕,但是第二天他发现蛋糕被老鼠咬坏了。
XP不想让蛋糕白白的被浪费,于是他把蛋糕分割成了一个个的小正三角形(如上图所示)。黑色的小正三角形表示老鼠把那一块咬坏了。XP想要切出一块最大的没被老鼠咬坏正三角形的蛋糕,可是最大的三角形有多大呢?
输入格式
第一行,一个整数N,表示XP把蛋糕纵向划分为N行。 接下来的N(N<=100)行,第i行包括了(n-i)*2+1个有效字符。“0”表示这块蛋糕是好的,“1”表示这块蛋糕被咬坏了。为了保持三角形的形状,输入文件中会出现空格。
输出格式
一行一个整数,表示最大的三角形包括的小三角形数。
样例
样例输入
5
101100001
0000010
00010
010
0
样例输出
9
题解
这道题让我不由自主地想起了盖房子,有兴趣可以看一看;
类比盖房子:
本题特殊之处:
首先需要预处理,将输入左对齐(过滤空格)(不左对齐也行,但左对齐后便于处理),这一步我处理的特别麻烦,大家作为参考,不要效仿(因为我也有些看不懂我写的。。。);
然后引入关于本题的一个重要结论:最大的三角形包括的小三角形数 = 高的平方(自己画几个图就能理解,在此不再赘述);
注意将1改成0,0改成1,便于以后加法操作;
相同之处:
其实这俩题思路一样,仍然是找最基本的小正三角形
1
111
如上,并将信息存储在最上方的小三角形中(存在哪都一样,因人而异),然后利用短板效应找min值更新信息,最后找出最大值即为答案;
但会有一个问题:
特殊数据
1000000
00001
000
0
注意中间的四个红色标记的小三角形,虽然满足上述情况,但并不能构成我们想要的正三角形;
发现问题出在三角形的朝向不一;
漏掉了向下的三角形;
1
111
因此我们需要处理两次,分别找出朝上和朝下的三角形最大值,最后找出总最大值即可;
代码
#include <iostream>
#include <cstring>
using namespace std;
int n;
char a[1005][1005];
int b[1005][1005];
int c[1005][1005];
int main() {
cin >> n;
int o = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= 2 * n - o; j++) {
cin >> a[i][j];
}
o += 2; //输入有些麻烦,尽量不要效仿;
}
o = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= 2 * n - o; j++) {
if (a[i][j] == '1') {
b[i][j] = 0;
}
if (a[i][j] == '0') {
b[i][j] = 1;
} //参考盖房子;将1换成0,0换成1;
}
o += 2;
} //左对齐;
memcpy(c, b, sizeof(b)); //复制一个新数组,便于以后操作两次;
o = 1;
int ans = -1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= 2 * n - o; j += 2) { //注意这里的j += 2,因为我们只找一个朝向的,而一个朝向隔一个出现一次;
if (b[i][j] && b[i - 1][j] && b[i - 1][j + 1] && b[i - 1][j + 2]) { //四个都是合法的才可能;
b[i][j] = min(b[i - 1][j], b[i - 1][j + 2]) + 1; //短板效应,找最小高度+本身高度(1);
}
ans = max(ans, b[i][j]); //ans找最大;
}
o += 2;
}
o = 2 * n - 1;
for (int i = n; i >= 1; i--) { //倒序循环找三角形尖向上的;
for (int j = 2; j <= 2 * n - o; j += 2) {
if (c[i][j] && c[i + 1][j] && c[i + 1][j - 1] && c[i + 1][j - 2]) {
c[i][j] = min(c[i + 1][j], c[i + 1][j - 2]) + 1;
}
ans = max(ans, c[i][j]);
}
o -= 2;
}
cout << ans * ans; //高的平方;
return 0;
}
后记:同机房大佬也写了这道题的题解。