题目描述
著名的格伦堡博物馆计划新建一个展区,专门展示杰出的计算机程序员的成就。新建筑的平面图是一个正交多边形(所有内角都是 90∘90^\circ90∘ 或 270∘270^\circ270∘)。用字符串描述多边形:RRR 表示 90∘90^\circ90∘ 角,OOO 表示 270∘270^\circ270∘ 角,按逆时针顺序列出。
博物馆有一个安全要求:必须存在一个位置,使得一个守卫可以看到整个楼层。对应的角度字符串被称为"可接受的"。
给定长度 LLL,求可接受的角度字符串的数量。
题目分析
角度字符串的基本性质
对于一个长度为 LLL 的正交多边形:
- 设 RRR 的数量为 rrr,OOO 的数量为 ooo
- 总内角和公式:90r+270o=(L−2)×18090r + 270o = (L-2) \times 18090r+270o=(L−2)×180
- 同时 r+o=Lr + o = Lr+o=L
解方程组:
{90r+270o=180(L−2)r+o=L
\begin{cases}
90r + 270o = 180(L-2) \\
r + o = L
\end{cases}
{90r+270o=180(L−2)r+o=L
解得:
- r=L+42r = \frac{L+4}{2}r=2L+4
- o=L−42o = \frac{L-4}{2}o=2L−4
结论1:LLL 必须为偶数,且 L≥4L \geq 4L≥4,r−o=4r - o = 4r−o=4。
可接受字符串的条件
要使多边形能被一个守卫完全看到(星形多边形),需要满足:
- 无连续两个 OOO:任何两个 270∘270^\circ270∘ 角不能相邻
- 环条件:首尾字符不能都是 OOO(因为多边形是环形的)
结论2:问题转化为在环形序列中放置 ooo 个 OOO 和 rrr 个 RRR,使得:
- 没有两个 OOO 相邻
- 首尾不能同时为 OOO
解题思路
动态规划方法
我们使用动态规划来统计满足条件的序列数量。
状态定义
设 dp[i][j][k]dp[i][j][k]dp[i][j][k] 表示:
- 已经处理了 iii 个字符
- 当前连续 RRR 的个数为 jjj(这个维度在最终解法中可以简化)
- 最后一个字符是否为 OOO(k=1k = 1k=1 表示是 OOO,k=0k = 0k=0 表示是 RRR)
但实际上,我们只需要知道已使用的 RRR 数量和最后一个字符类型。
状态转移
对于每个状态 (i,r_used,lastIsO)(i, r\_used, lastIsO)(i,r_used,lastIsO):
-
下一个放 RRR:
- 条件:r_used+1≤rr\_used + 1 \leq rr_used+1≤r
- 新状态:(i+1,r_used+1,0)(i+1, r\_used+1, 0)(i+1,r_used+1,0)
-
下一个放 OOO:
- 条件:!lastIsO!lastIsO!lastIsO 且 o_used+1≤oo\_used + 1 \leq oo_used+1≤o(其中 o_used=i−r_usedo\_used = i - r\_usedo_used=i−r_used)
- 新状态:(i+1,r_used,1)(i+1, r\_used, 1)(i+1,r_used,1)
环条件处理
由于多边形是环形的,我们需要分别处理两种情况:
- 以 RRR 开头:结尾可以是 RRR 或 OOO
- 以 OOO 开头:结尾必须是 RRR(不能首尾都是 OOO)
最终结果是两种情况的总和。
算法实现
// Glenbow Museum
// UVa ID: 1073
// Verdict: Accepted
// Submission Date: 2025-11-06
// UVa Run Time: 0.120s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <iostream>
#include <cstring>
using namespace std;
long long dp[1010][1010][2]; // dp[pos][r_count][last_is_O]
long long solve(int L) {
if (L < 4 || L % 2 != 0) return 0;
int R_count = (L + 4) / 2;
int O_count = (L - 4) / 2;
if (R_count < 0 || O_count < 0) return 0;
long long result = 0;
// 情况1:以 R 开头
memset(dp, 0, sizeof(dp));
dp[1][1][0] = 1;
for (int i = 1; i < L; i++) {
for (int r = 0; r <= i; r++) {
for (int lastO = 0; lastO <= 1; lastO++) {
if (dp[i][r][lastO] == 0) continue;
int o_used = i - r;
// 下一个是 R
if (r + 1 <= R_count) {
dp[i + 1][r + 1][0] += dp[i][r][lastO];
}
// 下一个是 O(不能连续两个 O)
if (!lastO && o_used + 1 <= O_count) {
dp[i + 1][r][1] += dp[i][r][lastO];
}
}
}
}
// 以 R 开头,结尾可以是 R 或 O
long long case1 = 0;
for (int lastO = 0; lastO <= 1; lastO++) {
case1 += dp[L][R_count][lastO];
}
// 情况2:以 O 开头
if (O_count > 0) {
memset(dp, 0, sizeof(dp));
dp[1][0][1] = 1;
for (int i = 1; i < L; i++) {
for (int r = 0; r <= i; r++) {
for (int lastO = 0; lastO <= 1; lastO++) {
if (dp[i][r][lastO] == 0) continue;
int o_used = i - r;
// 下一个是 R
if (r + 1 <= R_count) {
dp[i + 1][r + 1][0] += dp[i][r][lastO];
}
// 下一个是 O(不能连续两个 O)
if (!lastO && o_used + 1 <= O_count) {
dp[i + 1][r][1] += dp[i][r][lastO];
}
}
}
}
// 以 O 开头,必须以 R 结尾
long long case2 = dp[L][R_count][0];
result = case1 + case2;
} else {
result = case1;
}
return result;
}
int main() {
int L;
int caseNum = 1;
while (cin >> L && L != 0) {
long long ans = solve(L);
cout << "Case " << caseNum << ": " << ans << endl;
caseNum++;
}
return 0;
}
复杂度分析
- 时间复杂度:O(L3)O(L^3)O(L3),但由于 L≤1000L \leq 1000L≤1000,在优化下可以接受
- 空间复杂度:O(L2)O(L^2)O(L2)
样例验证
样例1:L=4L = 4L=4
- r=4r = 4r=4, o=0o = 0o=0
- 唯一序列:RRRR\texttt{RRRR}RRRR
- 输出:111 ✓
样例2:L=6L = 6L=6
- r=5r = 5r=5, o=1o = 1o=1
- 有效序列:666 种
- 输出:666 ✓
总结
本题的关键在于将几何问题转化为组合计数问题,通过分析正交多边形的角度关系和星形多边形的条件,得到字符串的约束条件,最后使用动态规划进行计数。这种方法避免了复杂的几何推理,直接通过组合数学解决问题。

1197

被折叠的 条评论
为什么被折叠?



