【BZOJ 1651】 [Usaco2006 Feb]Stall Reservations 专用牛棚(线段树,贪心)

博客围绕牛喝水问题展开,已知每头牛有特定喝水时间段且专用一个Stall,需确定满足要求的最少Stall数量。给出了题目描述、输入输出示例等,解题思路先是考虑线段树,后采用按起始点排序,用优先级队列维护结束点的贪心算法,还给出了两种解法的代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Description

Oh those picky N (1 <= N <= 50,000) cows! They are so picky that each one will only be milked over some precise time interval A…B (1 <= A <= B <= 1,000,000), which includes both times A and B. Obviously, FJ must create a reservation system to determine which stall each cow can be assigned for her milking time. Of course, no cow will share such a private moment with other cows. Help FJ by determining: * The minimum number of stalls required in the barn so that each cow can have her private milking period * An assignment of cows to these stalls over time

有N头牛,每头牛有个喝水时间,这段时间它将专用一个Stall 现在给出每头牛的喝水时间段,问至少要多少个Stall才能满足它们的要求

Input
  • Line 1: A single integer, N

  • Lines 2…N+1: Line i+1 describes cow i’s milking interval with two space-separated integers.

Output
  • Line 1: The minimum number of stalls the barn must have.

  • Lines 2…N+1: Line i+1 describes the stall to which cow i will be assigned for her milking period.

Sample Input

5
1 10
2 4
3 6
5 8
4 7

Sample Output

4

OUTPUT DETAILS:

Here’s a graphical schedule for this output:

Time 1 2 3 4 5 6 7 8 9 10

Stall 1 c1>>>>>>>>>>>>>>>>>>>>>>>>>>>

Stall 2 … c2>>>>>> c4>>>>>>>>> … …

Stall 3 … … c3>>>>>>>>> … … … …

Stall 4 … … … c5>>>>>>>>> … … …

Other outputs using the same number of stalls are possible.

HINT

不妨试下这个数据,对于按结束点SORT,再GREEDY的做法 1 3 5 7 6 9 10 11 8 12 4 13 正确的输出应该是3

原题传送门

思路

首先看到这道题,第一想法居然是线段树。。。
把所有区间覆盖了之后求覆盖的最大值就行了。
于是,用线段树很 (jian) (nan) 的水过去了。

直到看到这句话:
。。。
。。。。。。。。。。。。。。。。。。

好吧,纯属眼瞎。。。

我们按起始点排序,用一个优先级队列(大根堆)维护结束点,然后拿新的牛跟结束时间最靠前的元素比一下,如果新的牛可以喝水,队首改为当新的牛的结束点,否则就直接往队列里新加入当前结束点,最后把队列的大小打出即可。

代码

  • 解法1:线段树
//1、线段树
#include <bits/stdc++.h>
 
using namespace std;
const int M = 1010010;
struct node {
    int l, r, w, f;
} t[4*M];
int a, b, n, aa[50010], bb[50010];
 
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1)+(x << 3)+(ch ^ 48);
        ch = getchar();
    }
    return x*f;
}
 
void build(int k, int L, int R) {
    t[k].l = L, t[k].r = R;
    if (t[k].l == t[k].r) return ;
    int m = (L + R) / 2;
    build(k*2, L, m);
    build(k*2+1, m+1, R);
}
 
inline void down(int k) {
    t[k*2].f += t[k].f;
    t[k*2+1].f += t[k].f;
    t[k*2].w += t[k].f;
    t[k*2+1].w += t[k].f;
    t[k].f = 0;
}
 
void change_interval(int k) {
    if (t[k].l >= a && t[k].r <= b) {
        t[k].w++, t[k].f++;
        return ;
    }
    if(t[k].f) down(k);
    int m=(t[k].l+t[k].r)/2;
    if (a <= m) change_interval(k*2);
    if (b > m) change_interval(k*2+1);
    t[k].w = max(t[k*2].w, t[k*2+1].w);
}
 
int main() {
    ios::sync_with_stdio(false);
    int m = read(), n = -10;
    for (int i = 1; i <= m; i++)
        aa[i] = read(), bb[i] = read(), n = max(bb[i], n);
	build(1, 1, n);
	for (int i = 1; i <= m; i++)
		a = aa[i], b = bb[i], change_interval(1);
    cout << t[1].w << endl;
    return 0;
}
  • 解法2:贪心
//2、贪心
#include <bits/stdc++.h>

using namespace std;
struct node {
	int s, e;
} a[50010];
priority_queue <int, vector<int>,greater<int> > q;

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1)+(x << 3)+(ch ^ 48);
        ch = getchar();
    }
    return x*f;
}

bool cmp(const node &a, const node &b) {
	return a.s < b.s;
}

int main() {
	ios::sync_with_stdio(false);
	int n = read();
	for (int i = 1; i <= n; i++)
		a[i].s = read(), a[i].e = read();
	sort(a+1, a+1+n, cmp);
	for (int i = 1; i <= n; i++) {
		if (q.size() == 0) {q.push(a[i].e); continue;}
		if (q.top() < a[i].s) q.pop();
		q.push(a[i].e);
	}
	cout << q.size() << endl;
	return 0;
}
题目描述 牛牛和她的朋友们正在玩一个有趣的游戏,他们需要构建一个有 $n$ 个节点的无向图,每个节点都有一个唯一的编号并且编号从 $1$ 到 $n$。他们需要从节点 $1$ 到节点 $n$ 找到一条最短路径,其中路径长度是经过的边权的和。为了让游戏更有趣,他们决定在图上添加一些额外的边,这些边的权值都是 $x$。他们想知道,如果他们添加的边数尽可能少,最短路径的长度最多会增加多少。 输入格式 第一行包含两个正整数 $n$ 和 $m$,表示节点数和边数。 接下来 $m$ 行,每行包含三个整数 $u_i,v_i,w_i$,表示一条无向边 $(u_i,v_i)$,权值为 $w_i$。 输出格式 输出一个整数,表示最短路径的长度最多会增加多少。 数据范围 $2 \leq n \leq 200$ $1 \leq m \leq n(n-1)/2$ $1 \leq w_i \leq 10^6$ 输入样例 #1: 4 4 1 2 2 2 3 3 3 4 4 4 1 5 输出样例 #1: 5 输入样例 #2: 4 3 1 2 1 2 3 2 3 4 3 输出样例 #2: 2 算法 (BFS+最短路) $O(n^3)$ 我们把问题转化一下,假设原图中没有添加边,所求的就是点 $1$ 到点 $n$ 的最短路,并且我们已经求出了这个最短路的长度 $dis$。 接下来我们从小到大枚举边权 $x$,每次将 $x$ 加入图中,然后再次求解点 $1$ 到点 $n$ 的最短路 $dis'$,那么增加的最短路长度就是 $dis'-dis$。 我们发现,每次加入一个边都需要重新求解最短路。如果我们使用 Dijkstra 算法的话,每次加入一条边需要 $O(m\log m)$ 的时间复杂度,总的时间复杂度就是 $O(m^2\log m)$,无法通过本题。因此我们需要使用更优秀的算法。 观察到 $n$ 的范围比较小,我们可以考虑使用 BFS 求解最短路。如果边权均为 $1$,那么 BFS 可以在 $O(m)$ 的时间复杂度内求解最短路。那么如果我们只是加入了一条边的话,我们可以将边权为 $x$ 的边看做 $x$ 条边的组合,每次加入该边时,我们就在原始图上添加 $x$ 条边,边权均为 $1$。这样,我们就可以使用一次 BFS 求解最短路了。 但是,我们不得不考虑加入多条边的情况。如果我们还是将边权为 $x$ 的边看做 $x$ 条边的组合,那么我们就需要加入 $x$ 条边,而不是一条边。这样,我们就不能使用 BFS 了。 但是,我们可以使用 Floyd 算法。事实上,我们每次加入边时,只有边权等于 $x$ 的边会发生变化。因此,如果我们枚举边权 $x$ 时,每次只需要将边权等于 $x$ 的边加入图中,然后使用 Floyd 算法重新计算最短路即可。由于 Floyd 算法的时间复杂度为 $O(n^3)$,因此总的时间复杂度为 $O(n^4)$。 时间复杂度 $O(n^4)$ 空间复杂度 $O(n^2)$ C++ 代码 注意点:Floyd算法计算任意两点之间的最短路径,只需要在之前的路径基础上加入新的边构成的新路径进行更新即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值