UVa 1073 Glenbow Museum

题目描述

著名的格伦堡博物馆计划新建一个展区,专门展示杰出的计算机程序员的成就。新建筑的平面图是一个正交多边形(所有内角都是 90∘90^\circ90270∘270^\circ270)。用字符串描述多边形:RRR 表示 90∘90^\circ90 角,OOO 表示 270∘270^\circ270 角,按逆时针顺序列出。

博物馆有一个安全要求:必须存在一个位置,使得一个守卫可以看到整个楼层。对应的角度字符串被称为"可接受的"。

给定长度 LLL,求可接受的角度字符串的数量。

题目分析

角度字符串的基本性质

对于一个长度为 LLL 的正交多边形:

  • RRR 的数量为 rrrOOO 的数量为 ooo
  • 总内角和公式:90r+270o=(L−2)×18090r + 270o = (L-2) \times 18090r+270o=(L2)×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(L2)r+o=L

解得:

  • r=L+42r = \frac{L+4}{2}r=2L+4
  • o=L−42o = \frac{L-4}{2}o=2L4

结论1LLL 必须为偶数,且 L≥4L \geq 4L4r−o=4r - o = 4ro=4

可接受字符串的条件

要使多边形能被一个守卫完全看到(星形多边形),需要满足:

  1. 无连续两个 OOO:任何两个 270∘270^\circ270 角不能相邻
  2. 环条件:首尾字符不能都是 OOO(因为多边形是环形的)

结论2:问题转化为在环形序列中放置 oooOOOrrrRRR,使得:

  • 没有两个 OOO 相邻
  • 首尾不能同时为 OOO

解题思路

动态规划方法

我们使用动态规划来统计满足条件的序列数量。

状态定义

dp[i][j][k]dp[i][j][k]dp[i][j][k] 表示:

  • 已经处理了 iii 个字符
  • 当前连续 RRR 的个数为 jjj(这个维度在最终解法中可以简化)
  • 最后一个字符是否为 OOOk=1k = 1k=1 表示是 OOOk=0k = 0k=0 表示是 RRR

但实际上,我们只需要知道已使用的 RRR 数量和最后一个字符类型。

状态转移

对于每个状态 (i,r_used,lastIsO)(i, r\_used, lastIsO)(i,r_used,lastIsO)

  1. 下一个放 RRR

    • 条件:r_used+1≤rr\_used + 1 \leq rr_used+1r
    • 新状态:(i+1,r_used+1,0)(i+1, r\_used+1, 0)(i+1,r_used+1,0)
  2. 下一个放 OOO

    • 条件:!lastIsO!lastIsO!lastIsOo_used+1≤oo\_used + 1 \leq oo_used+1o(其中 o_used=i−r_usedo\_used = i - r\_usedo_used=ir_used
    • 新状态:(i+1,r_used,1)(i+1, r\_used, 1)(i+1,r_used,1)
环条件处理

由于多边形是环形的,我们需要分别处理两种情况:

  1. RRR 开头:结尾可以是 RRROOO
  2. 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 1000L1000,在优化下可以接受
  • 空间复杂度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

总结

本题的关键在于将几何问题转化为组合计数问题,通过分析正交多边形的角度关系和星形多边形的条件,得到字符串的约束条件,最后使用动态规划进行计数。这种方法避免了复杂的几何推理,直接通过组合数学解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值