solution of POJ 2376.Cleaning Shifts

解决农场主John的问题,即如何用最少数量的奶牛完成一天内的清洁任务分配,确保每个时间段都有奶牛工作。

Description

Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning chores around the barn. He always wants to have one cow working on cleaning things up and has divided the day into T shifts (1 <= T <= 1,000,000), the first being shift 1 and the last being shift T.

Each cow is only available at some interval of times during the day for work on cleaning. Any cow that is selected for cleaning duty will work for the entirety of her interval.

Your job is to help Farmer John assign some cows to shifts so that (i) every shift has at least one cow assigned to it, and (ii) as few cows as possible are involved in cleaning. If it is not possible to assign a cow to each shift, print -1.

Input

Line 1: Two space-separated integers: N and T

Lines 2..N+1: Each line contains the start and end times of the interval during which a cow can work. A cow starts work at the start time and finishes after the end time.

Output

Line 1: The minimum number of cows Farmer John needs to hire or -1 if it is not possible to assign a cow to each shift.

Sample Input

3 10
1 7
3 6
6 10

Sample Output

2


结题思路
题意要求我们给出能够覆盖指定区间的最少的工作人员。
要求1:如果一个工作人员的工作时间区间覆盖越大,选择他会让我们最后的总人数经可能得降低;
要求2:如果一个工作人员的结束时间为end,则下一个工作人员的开始时间必须小于等于end + 1,结束时间必须大于等于end + 1。

程序步骤
第一步、根据开始时间对所有的工作人员进行递增排序,开始时间相同的情况下对结束时间进行递增排序。
第二步、贪心法遍历数组,找到全部小于等于下一个可行的开始时间的工作人员,选择结束时间最晚的工作人员。


具体程序(AC)如下

#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
struct cow
{
    int start;
    int end;
};
int cmp(cow& a, cow& b)
{
    if(a.start != b.start)
        return a.start < b.start;
    else
        return a.end < b.end;
}
int solve(vector<cow>& cowList, int t)
{
    int index = 0;
    int counter = 0;
    int start;
    int end = 0;
    int len = cowList.size();
    while(end < t)
    {
        start = end + 1;//下一个最晚的开始时间
        for(int i = index; i < len; ++i)
        {
            if(start >= cowList[i].start) 
            {
                if(start <= cowList[i].end && end < cowList[i].end)//结束时间更新条件
                    end = cowList[i].end;
            }
            else
            {
                index = i;//下一次循环开始的索引编号
                break;
            }
        }
        if(start > end)//结束时间未得到更新,说明没有找到合适的工作人员
            return -1;
        else
            ++counter;
    }
    return counter;
}
int main()
{
    int n, t;
    cin>> n >>t;
    vector<cow> cowList(n);
    for(int i = 0; i < n; ++i)
        cin>> cowList[i].start >> cowList[i].end;
    sort(cowList.begin(), cowList.end(), cmp);
    cout<<solve(cowList, t)<<endl;
    return 0;
}
<think>好的,我现在要解决用户关于POJ1986 Distance Queries的问题。首先,我需要确认这个题目的具体内容和要求。用户可能是在准备算法竞赛或者学习数据结构,需要了解这个问题的解决方法。 POJ1986的题目名称是Distance Queries,应该属于最近公共祖先(LCA)或树上的路径查询问题。这类问题通常需要预处理树结构,以便快速回答两点之间的距离。用户可能需要知道如何高效处理多次查询。 接下来,我需要回忆或查找POJ1986的具体描述。根据经验,这类问题通常是给定一棵无向树,多次询问两点之间的最短距离。因为树中任意两点路径唯一,所以距离可以通过LCA来计算。距离公式一般是dist(u, v) = dist(root, u) + dist(root, v) - 2 * dist(root, lca(u, v))。 接下来,我需要考虑如何实现。通常有两种方法:Tarjan的离线算法和倍增法的在线算法。Tarjan算法在处理所有查询时效率很高,但需要提前知道所有查询点。而倍增法则允许在线处理,每次查询的时间复杂度是O(log n),适合处理大量随机查询。 然后,思考具体步骤。首先,构建树的邻接表,记录每个节点的子节点和边的权值。接着,预处理每个节点的深度和到根节点的距离。对于倍增法,还需要预处理每个节点向上2^k步的祖先节点。这一步通常通过DFS或BFS完成。 预处理完成后,对于每个查询(u, v),找到它们的LCA,然后利用之前计算的到根节点的距离来计算两点间的路径长度。这一步需要确保LCA查找的正确性,否则计算结果会出错。 可能需要注意的细节包括树的根节点选择(通常选1号节点),以及处理输入数据时可能有无关的边方向信息,但题目可能给出的是无向树,因此邻接表需要双向添加,但遍历时要记录父节点避免回溯。 另外,输入数据中可能有冗余信息,比如题目中的“路”和“方向”,但实际可以忽略方向,因为树是无向的。需要提醒用户注意处理输入时只需提取有效信息。 测试样例的处理也很重要,例如当u和v相同时,距离应为0;当其中一个是另一个的祖先时,计算是否准确。需要确保代码能处理这些边界情况。 最后,总结解决方法,给出算法步骤和复杂度分析。时间复杂度主要是预处理O(n log n)和每次查询O(log n),空间复杂度O(n log n)用于存储倍增表。这样的复杂度对于n到4e4和查询数到1e4的情况应该是可以接受的。 用户可能需要具体的代码实现,但根据要求,不需要提供代码,而是解释思路。所以应该分步骤说明解决过程,确保用户理解如何应用LCA和距离计算的方法。 可能遇到的常见错误包括忘记处理双向边导致死循环,或者预处理时没有正确计算每个节点的深度和距离。需要提醒用户注意这些点。 总结来说,回答应该包括问题分析、解决方法(LCA的应用)、算法步骤、复杂度说明以及可能的注意事项,帮助用户全面理解如何解决POJ1986的问题。</think>### POJ1986 Distance Queries 问题详解 #### 问题背景 题目要求计算树结构中多个节点对之间的最短路径长度。给定一棵包含 $N$ 个节点(节点编号从 $1$ 开始)的无向树,每条边带有权值。需回答 $M$ 次查询,每次给出两个节点 $u$ 和 $v$,输出它们之间的最短距离。 #### 核心思路:LCA + 前缀和 1. **树的性质**:树中任意两节点间有唯一路径,最短距离即路径边权之和。 2. **LCA(最近公共祖先)**:路径必经两节点的最近公共祖先。设 $dist[u]$ 表示根节点到 $u$ 的路径长度,则两点间距离公式为: $$dist(u, v) = dist[u] + dist[v] - 2 \cdot dist[LCA(u, v)]$$ 3. **倍增法求LCA**:通过预处理每个节点向上跳 $2^k$ 步的祖先,实现 $O(\log n)$ 时间复杂度的查询。 --- #### 解决步骤 **1. 树的存储与预处理** - **邻接表存树**:记录每个节点的相邻节点及边权。 - **DFS/BFS预处理**: - 计算每个节点的深度 $depth[u]$ 和到根节点距离 $dist[u]$。 - 构建倍增表 $up[u][k]$,表示节点 $u$ 向上跳 $2^k$ 步的祖先。 **2. LCA查询实现** - **调整深度对齐**:将较深节点上跳至与另一节点同深度。 - **同步上跳找LCA**:从最大步长开始尝试,若祖先不同则上跳,直至找到共同祖先。 **3. 距离计算** - 根据公式直接计算,无需实际遍历路径。 --- #### 复杂度分析 - **预处理**:DFS/BFS遍历 $O(N)$,倍增表构建 $O(N \log N)$。 - **单次查询**:$O(\log N)$。 - **总复杂度**:$O(N \log N + M \log N)$,适用于 $N \leq 4 \times 10^4$ 和 $M \leq 10^4$ 的规模。 --- #### 关键实现细节 1. **根节点选择**:通常选择 $1$ 号节点为根,需保证树连通。 2. **输入处理**:忽略无关的方向信息,按无向边构建邻接表。 3. **倍增表初始化**: - $up[u][0]$ 直接父节点。 - 递推公式:$up[u][k] = up[ up[u][k-1] ][k-1]$。 4. **边界条件**: - 查询的两个节点相同时,距离为 $0$。 - 节点为对方祖先时需正确触发上跳终止条件。 --- #### 示例说明 **输入**: ``` 7 6 1 6 13 E 6 3 9 E 3 5 7 S 4 1 3 N 2 4 20 W 4 7 2 S 3 1 6 1 4 2 6 ``` **处理过程**: 1. 建树后,根为 $1$,计算各节点 $dist$ 值。 2. 查询 $2$ 和 $6$: - LCA(2,6) 为 $4$。 - 距离 = $dist[2] + dist[6] - 2 \cdot dist[4] = 3 + 13 - 2 \cdot 3 = 16$。 --- #### 总结 POJ1986 是经典的LCA应用问题,通过结合倍增法和前缀和,能够高效处理树上的路径查询。需注意树的存储、预处理细节及边界条件,代码实现时可选择DFS或BFS进行初始化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值