UVa 11493 The Club Ballroom

问题描述

Tingua\texttt{Tingua}Tingua 社交俱乐部要为新舞厅铺设木地板。他们收到了一批捐赠的木板,这些木板宽度相同LLL 厘米),但长度不同。舞厅是一个 N×MN \times MN×M 米的长方形区域。铺设木板时需要满足以下限制:

  1. 方向一致:所有木板必须平行放置,要么全部横向(长度方向平行于 NNN 边),要么全部纵向(长度方向平行于 MMM 边)。
  2. 无重叠:木板必须并排铺设,不能重叠,完全覆盖整个地板。
  3. 拼接限制:每行(横向时)或每列(纵向时)最多只能用两块木板拼接完成。也就是说,如果一块木板长度不够,最多只能与另一块木板拼接。
  4. 不切割:不能锯断任何木板。

目标:判断是否能用捐赠的木板覆盖整个舞厅地板,如果可能,求出所需的最少木板数量。

输入格式

  • 每个测试用例包含:
    1. 两个整数 NNNMMM (1≤N,M≤104)(1 \leq N,M \leq 10^4)(1N,M104),表示舞厅尺寸(米)。
    2. 一个整数 LLL (1≤L≤100)(1 \leq L \leq 100)(1L100),表示木板宽度(厘米)。
    3. 一个整数 KKK (1≤K≤105)(1 \leq K \leq 10^5)(1K105),表示木板数量。
    4. KKK 个整数 XiX_iXi (1≤Xi≤104)(1 \leq X_i \leq 10^4)(1Xi104),表示每块木板的长度(米)。
  • 输入以 0 0 结束。

输出格式

对于每个测试用例,输出一行:

  • 如果可能覆盖,输出最少木板数量。
  • 如果不可能,输出 impossivel(葡萄牙语的“不可能”)。

解题思路

核心问题分解

这个问题实际上可以分解为两个独立的子问题,分别对应两种木板放置方向:

方向1:木板横向放置

  • 木板长度方向平行于 NNN 边。
  • 需要覆盖 MMM 米宽的边,木板宽度为 L/100L/100L/100 米。
  • 如果 MMM 不能被 L/100L/100L/100 整除,这个方向直接不可能。
  • 需要的“行数”:numLines=M/(L/100)numLines = M / (L/100)numLines=M/(L/100)
  • 每一行的长度必须是 NNN 米。
  • 每一行只能用 111 块或 222 块木板:
    • 111 块:木板长度正好为 NNN 米。
    • 222 块:两块木板长度之和为 NNN 米。

方向2:木板纵向放置

  • 木板长度方向平行于 MMM 边。
  • 需要覆盖 NNN 米宽的边。
  • 需要满足的条件与方向1类似,只是 NNNMMM 角色互换。

算法设计

1. 方向检查函数

我们需要一个函数 tryDirection(roomLength, roomWidth, L, planks)

  • roomLength:木板需要覆盖的长度(米)。
  • roomWidth:需要铺设的行/列总宽度(米)。
  • 返回:覆盖所需的最少木板数,如果不可能返回 −1-11

2. 避免浮点精度问题

由于 LLL 是厘米,而房间尺寸是米,直接使用浮点数计算 L/100L/100L/100 可能导致精度问题。更好的方法是将所有单位统一转换为厘米

  • 房间长度:roomLength×100roomLength \times 100roomLength×100 厘米
  • 房间宽度:roomWidth×100roomWidth \times 100roomWidth×100 厘米
  • 木板宽度:LLL 厘米
  • 木板长度:Xi×100X_i \times 100Xi×100 厘米

这样所有计算都在整数范围内进行,避免了浮点误差。

3. 木板分配策略(贪心算法)

对于每一行/列,我们按以下优先级分配木板:

  1. 优先使用单块完整木板:如果有长度正好等于目标长度的木板,直接使用。
  2. 使用两块木板拼接:如果没有完整长度的木板,寻找一对木板,它们的长度之和等于目标长度。

为什么贪心策略有效?

  • 使用单块木板总是最优的(节省一块木板)。
  • 当需要拼接时,任意一对长度之和等于目标长度的木板都是等价的。
  • 我们只需要关心木板数量,不关心具体哪两块木板拼接。

4. 实现细节

  • 使用 map<int, int> 统计每种长度(厘米)的木板数量。
  • 对于每一行/列,尝试分配木板:
    • 检查是否有完整长度的木板。
    • 如果没有,遍历所有可能的木板长度 aaa,寻找 b=target−ab = target - ab=targeta
    • 注意处理 a=ba = ba=b 的情况(需要两块相同长度的木板)。
  • 如果某一无法分配木板,该方向不可能。

5. 复杂度分析

  • 木板数量 K≤105K \leq 10^5K105
  • 对于每个方向,最坏情况下需要遍历所有木板寻找拼接对。
  • 使用 map 查找和更新是 O(log⁡K)O(\log K)O(logK)
  • 总体复杂度:O(Klog⁡K)O(K \log K)O(KlogK),可以接受。

边界情况考虑

  1. 木板宽度不能整除房间宽度:直接不可能。
  2. 木板长度不足:即使拼接也无法达到目标长度。
  3. 木板数量不足:即使每行/列都能分配,但总行/列数需要的木板数超过 KKK
  4. 大整数运算:全部转换为厘米后,最大值为 104×100=10610^4 \times 100 = 10^6104×100=106,在 int 范围内。

参考代码

// The Club Ballroom
// UVa ID: 11493
// Verdict: Accepted
// Submission Date: 2025-12-01
// UVa Run Time: 0.060s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

// 尝试覆盖一个方向,返回最少木板数,如果不可能返回-1
int tryDirection(int roomLength, int roomWidth, int L, vector<int>& planks) {
    // 全部转换为厘米
    int lengthCm = roomLength * 100;
    int widthCm = roomWidth * 100;
    
    // 检查木板宽度是否能整除房间宽度
    if (widthCm % L != 0) return -1;
    
    int numLines = widthCm / L;
    
    // 统计木板数量(厘米)
    map<int, int> count;
    for (int len : planks) {
        count[len * 100]++;
    }
    
    map<int, int> tempCount = count;
    int totalUsed = 0;
    
    for (int i = 0; i < numLines; i++) {
        // 优先使用单块完整木板
        if (tempCount[lengthCm] > 0) {
            tempCount[lengthCm]--;
            totalUsed++;
            continue;
        }
        
        // 尝试用两块木板拼接
        bool found = false;
        for (auto& p : tempCount) {
            int a = p.first;
            if (a >= lengthCm) continue;
            
            int b = lengthCm - a;
            if (b <= 0) continue;
            
            if (a == b) {
                if (p.second >= 2) {
                    tempCount[a] -= 2;
                    if (tempCount[a] == 0) tempCount.erase(a);
                    totalUsed += 2;
                    found = true;
                    break;
                }
            } else {
                if (tempCount.find(b) != tempCount.end() && tempCount[b] > 0) {
                    tempCount[a]--;
                    tempCount[b]--;
                    if (tempCount[a] == 0) tempCount.erase(a);
                    if (tempCount[b] == 0) tempCount.erase(b);
                    totalUsed += 2;
                    found = true;
                    break;
                }
            }
        }
        
        if (!found) return -1;
    }
    
    return totalUsed;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N, M;
    while (cin >> N >> M, N || M) {
        int L, K;
        cin >> L >> K;
        vector<int> planks(K);
        for (int i = 0; i < K; i++) cin >> planks[i];
        
        int ans = INT_MAX;
        
        // 方向1:木板横向放置
        int result1 = tryDirection(N, M, L, planks);
        if (result1 != -1) ans = min(ans, result1);
        
        // 方向2:木板纵向放置
        int result2 = tryDirection(M, N, L, planks);
        if (result2 != -1) ans = min(ans, result2);
        
        if (ans == INT_MAX) cout << "impossivel\n";
        else cout << ans << "\n";
    }
    
    return 0;
}

代码说明

  1. 单位转换:将所有尺寸统一转换为厘米,避免浮点精度问题。
  2. 木板统计:使用 map<int, int> 统计每种长度的木板数量,便于查找和更新。
  3. 贪心分配:对于每一行/列,优先使用完整木板,其次寻找拼接对。
  4. 双方向检查:分别检查横向和纵向放置的可能性,取最小值。
  5. 输入优化:使用 ios::sync_with_stdio(false)cin.tie(nullptr) 加快输入速度。

总结

本题的关键在于理解木板铺设的限制条件,特别是方向一致最多两块拼接的限制。通过将问题分解为两个独立的方向,并对每个方向使用贪心策略分配木板,可以高效求解。注意单位转换避免浮点精度问题,以及合理选择数据结构提高效率。

The ICPC world finals will be held in a luxurious hotel with a big ballroom. A buffet meal will be served in this ballroom, and organizers decided to decorate its walls with pictures of past champion teams. In order to avoid criticism about favouring some of those teams over others, the organizing commitee wants to make sure that all pictures are appropiately illuminated. The only direct way they’ve found for doing this is ensuring each picture has at least one lightbulb that directly illuminates it. In this way, the perimeter of the ballroom wall can be divided into illuminated parts (in which pictures may be placed) and dark parts (which are not suitable for placing the pictures). The ballroom has the shape of a box and contains several lightbulbs. Each lightbulb emits light in all directions, but this light can be blocked by columns. All columns in the ballroom have cylindrical shape and go from the floor to the ceiling, so light cannot pass over or above them. Columns are of course placed so that its circular section is parallel to the ballroom floor. Any given point p on the perimeter wall is said to be illuminated if there exists a line segment (a light ray) which starts on a lightbulb, ends in p and does not touch or pass through any column. Your task as a helper of the ICPC organization is to examine the blueprints of the ballroom and determine the total length of illuminated sections of the perimeter wall. The blueprint consist of a rectangle indicating a top view of the ballroom, with the lightbulbs and columns marked in it. 输入 Each test case will consist on several lines. The first line will contain four integers: L, the number of lightbulbs, C, the number of columns, X, the size of the ballroom on the x coordinate and Y , the size of the ballroom on the y coordinate. The lower-left corner of the ballroom is at (0, 0) while the upper-right corner is at (X, Y ). The next L lines will contain two integers each representing the x and y coordinate of each lightbulb. The last C lines of the test case will contain three integers each, representing the x and y coordinates of the center of a column and its radius, in that order. You can assume that 1 <= L,C <= 103 and 4 <= X, Y <= 106. Also, for all pairs of coordinates (x,y), 0 < x < X and 0 < y < Y , both for lightbulbs and column center locations. All radii of the columns will be positive. Finally, no two columns will overlap, although they may touch, and no column will touch or intersect with the border of the ballroom. No lightbulb will be inside a column or in its boundary and no two lightbulbs will be in the same place. Input is terminated with L = C = X = Y = 0. 输出 For each test case, output a single line with the total length of the illuminated parts of the perimeter wall. The result must be printed as a real number with exactly four decimal figures, with the lowest-order decimal figure rounded up. 样例输入 2 1 8 8 6 6 2 6 4 4 2 1 4 7 7 3 3 2 4 1 4 2 1 2 2 1 4 4 1 2 2 9 7 1 2 5 5 3 3 2 7 5 1 0 0 0 0 ⧉ 样例输出 28.0000 0.0000 25.8214 ⧉ 题目来源 用巧思
11-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值