#P2769. 第2题-二进制字符串
-
1000ms
Tried: 27
Accepted: 12
Difficulty: 6
所属公司 :
米哈游
时间 :2025年3月29日
-
算法标签>动态规划
第2题-二进制字符串
思路与做法
-
索引消模:用 s2=s+s,则 Ar,c=s2[(n−r)+c],避免每格做取模。
-
最大矩形(全 0):维护直方图高度 H[c](当前行该列为 0 则 H[c]←H[c]+1,否则 H[c]←0),对每一行用单调栈在 O(n) 求“柱状图最大矩形”,总 O(n2)。
-
最大直角等腰三角形(全 0):两向 DP,自底向上、滚动一行,空间 O(n)、时间 O(n2)。
- 向右下(顶点在左上):

- 向左下(顶点在右上):

- 每得高 h,面积为 h(h+1)/2,实时更新最大值。
-
答案:矩形最大面积与两向三角形最大面积取最大。
-
复杂度:时间 O(n2),空间 O(n)。
C++
#include <bits/stdc++.h>
using namespace std;
// 手写数组栈,常数更小
static int st_arr[5005];
// 单行直方图最大矩形
inline int maxRect(vector<int>& h) {
int n = (int)h.size(), top = -1, ans = 0;
for (int i = 0; i <= n; ++i) {
int cur = (i == n ? 0 : h[i]);
while (top >= 0 && h[st_arr[top]] > cur) {
int height = h[st_arr[top--]];
int left = (top >= 0 ? st_arr[top] : -1);
int width = i - left - 1;
int area = height * width;
if (area > ans) ans = area;
}
st_arr[++top] = i;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
if (!(cin >> s)) return 0;
int n = (int)s.size();
string s2 = s + s; // A_{r,c} = s2[(n - r) + c]
// 1) 最大矩形
vector<int> height(n, 0);
int bestRect = 0;
for (int r = 0; r < n; ++r) {
int base = n - r;
const char* rowp = s2.data() + base;
for (int c = 0; c < n; ++c) {
if (rowp[c] == '0') height[c] += 1;
else height[c] = 0;
}
int val = maxRect(height);
if (val > bestRect) bestRect = val;
}
// 2) 最大三角形(两向 DP)
long long bestTri = 0;
vector<int> nextDR(n, 0), curDR(n, 0);
vector<int> nextDL(n, 0), curDL(n, 0);
for (int r = n - 1; r >= 0; --r) {
int base = n - r;
const char* rowp = s2.data() + base;
for (int c = 0; c < n; ++c) {
if (rowp[c] == '0') {
int v1 = nextDR[c];
int v2 = (c + 1 < n ? nextDR[c + 1] : 0);
int h1 = 1 + (v1 < v2 ? v1 : v2);
curDR[c] = h1;
long long a1 = 1LL * h1 * (h1 + 1) / 2;
if (a1 > bestTri) bestTri = a1;
int w1 = nextDL[c];
int w2 = (c - 1 >= 0 ? nextDL[c - 1] : 0);
int h2 = 1 + (w1 < w2 ? w1 : w2);
curDL[c] = h2;
long long a2 = 1LL * h2 * (h2 + 1) / 2;
if (a2 > bestTri) bestTri = a2;
} else {
curDR[c] = 0;
curDL[c] = 0;
}
}
nextDR.swap(curDR);
nextDL.swap(curDL);
}
long long ans = max<long long>(bestRect, bestTri);
cout << ans << '\n';
return 0;
}
Python
import sys
def max_rect(h):
# 单调栈求直方图最大矩形
st = []
ans = 0
n = len(h)
for i in range(n + 1):
cur = 0 if i == n else h[i]
while st and h[st[-1]] > cur:
height = h[st.pop()]
left = -1 if not st else st[-1]
width = i - left - 1
area = height * width
if area > ans:
ans = area
st.append(i)
return ans
def solve():
s = sys.stdin.readline().strip()
n = len(s)
s2 = s + s # A[r][c] = s2[(n - r) + c]
# 1) 最大矩形
height = [0] * n
best_rect = 0
for r in range(n):
base = n - r
row = s2[base:base + n]
for c in range(n):
if row[c] == '0':
height[c] += 1
else:
height[c] = 0
val = max_rect(height)
if val > best_rect:
best_rect = val
# 2) 最大三角形(两向 DP)
best_tri = 0
nextDR = [0] * n
nextDL = [0] * n
curDR = [0] * n
curDL = [0] * n
for r in range(n - 1, -1, -1):
base = n - r
row = s2[base:base + n]
for c in range(n):
if row[c] == '0':
v1 = nextDR[c]
v2 = nextDR[c + 1] if c + 1 < n else 0
h1 = 1 + (v1 if v1 < v2 else v2)
curDR[c] = h1
a1 = h1 * (h1 + 1) // 2
if a1 > best_tri:
best_tri = a1
w1 = nextDL[c]
w2 = nextDL[c - 1] if c - 1 >= 0 else 0
h2 = 1 + (w1 if w1 < w2 else w2)
curDL[c] = h2
a2 = h2 * (h2 + 1) // 2
if a2 > best_tri:
best_tri = a2
else:
curDR[c] = 0
curDL[c] = 0
nextDR, curDR = curDR, nextDR
nextDL, curDL = curDL, nextDL
print(max(best_rect, best_tri))
if __name__ == "__main__":
solve()
Java
import java.io.*;
import java.util.*;
public class Main {
static int maxRect(int[] h) {
int n = h.length;
Deque<Integer> st = new ArrayDeque<>();
int ans = 0;
for (int i = 0; i <= n; i++) {
int cur = (i == n ? 0 : h[i]);
while (!st.isEmpty() && h[st.peek()] > cur) {
int height = h[st.pop()];
int left = st.isEmpty() ? -1 : st.peek();
int width = i - left - 1;
int area = height * width;
if (area > ans) ans = area;
}
st.push(i);
}
return ans;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = br.readLine().trim();
int n = s.length();
String s2 = s + s; // A[r][c] = s2[(n - r) + c]
// 1) 最大矩形
int[] height = new int[n];
int bestRect = 0;
for (int r = 0; r < n; r++) {
int base = n - r;
// 直接按字符访问
for (int c = 0; c < n; c++) {
char ch = s2.charAt(base + c);
if (ch == '0') height[c]++;
else height[c] = 0;
}
bestRect = Math.max(bestRect, maxRect(height));
}
// 2) 最大三角形(两向 DP)
long bestTri = 0;
int[] nextDR = new int[n], curDR = new int[n];
int[] nextDL = new int[n], curDL = new int[n];
for (int r = n - 1; r >= 0; r--) {
int base = n - r;
for (int c = 0; c < n; c++) {
char ch = s2.charAt(base + c);
if (ch == '0') {
int v1 = nextDR[c];
int v2 = (c + 1 < n ? nextDR[c + 1] : 0);
int h1 = 1 + Math.min(v1, v2);
curDR[c] = h1;
long a1 = 1L * h1 * (h1 + 1) / 2;
if (a1 > bestTri) bestTri = a1;
int w1 = nextDL[c];
int w2 = (c - 1 >= 0 ? nextDL[c - 1] : 0);
int h2 = 1 + Math.min(w1, w2);
curDL[c] = h2;
long a2 = 1L * h2 * (h2 + 1) / 2;
if (a2 > bestTri) bestTri = a2;
} else {
curDR[c] = 0;
curDL[c] = 0;
}
}
int[] tmp;
tmp = nextDR; nextDR = curDR; curDR = tmp;
tmp = nextDL; nextDL = curDL; curDL = tmp;
}
long ans = Math.max(bestRect, bestTri);
System.out.println(ans);
}
}
题目内容
给定一个长度为n的二进制字符串s,由0和1字符组成。我们需要构建一个行数为n,列数为n的方表,由0和1字符组成。第一行为原始字符串 s,第二行为字符串s向右循环移动一个,第三行为字符串s向右循环移动两个,以此类推。
求表中所有由0组成的三角形或矩形的最大面积值。
第一行是字符串s。
第二行是字符串s向右循环移动一个位置。
第i行是字符串s向右循环移动i−1个位置。
输入描述
输入一个长度为n的二进制字符串s,仅包含0和1字符,其中1≤n≤5000
输出描述
在一行上输出n个整数,代表对于每一个i的答案。
样例1
输入
00110
输出
6
说明
在构造的方表中,最大由0组成的三角形面积为6,构造的表格如下:
00110
00011
10001
11000
01100