题解 喷水装置

喷水装置


题目描述

在一个长 L L L 米,宽 W W W 米的草坪里装有 N N N 个浇灌喷头。
每个喷头都装在草坪中心线上 (离两边各 W 2 \frac W 2 2W 米)
我们知道每个喷头的位置 C C C (离草坪中心线左端的距离),以及它能覆盖到的浇灌范围 R R R
请问:最少需要打开多少个喷头才能浇灌整块草坪?喷水装置 —— 图

输入格式

输入包含若干组测试数据。
第一行一个整数 T T T,表示数据的组数;
每组数据的第一行是整数 N N N L L L W W W
接下来的 行,每行包含两个整数,给出一个喷头的位置和浇灌半径 (上面的示意图是样例输入第一组数据所描述的情况)。

输出格式

对每组测试数据输出一个数字,表示为浇灌整块草坪所需喷头数目的最小值。
如果所有喷头都打开也不能浇灌整块草坪,则输出 − 1 -1 1

样例
样例输入

3

8 20 2
5 3
4 1
1 2
7 2
10 2
13 3
16 2
19 4

3 10 1
3 5
9 3
6 1

3 10 1
5 3
1 1
9 1

样例输出

6
2
-1

数据范围

对于 100 100% 100 的数据, N < = 15000 N <= 15000 N<=15000

提示

贪心


分析

对于任意一个喷水装置,它能喷洒到的区域是一个已知半径的圆形。
但是喷洒到草坪上的区域,则是近似一个矩形的。
这个矩形就是圆形与草坪的四个交点所围成的矩形。 (在这里兔兔不方便画图,就需要读者自己在草稿本上画图了)
我们可以求出每个喷水装置在草坪上喷洒的区域 (之前所说的矩形) 的 左端点右端点
然后在所有左端点小于上一个区间右端点的区域中选择右端点最大的一个喷水装置。
剩下的分析,就附在代码上了


代码

下面是正解代码:

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

const int MAXN = 15005;
int T;
int N, L, W;
struct node {
	double l, r;
}P[MAXN];
int cnt;

int main()
{
	while (scanf("%d", &T) != EOF)
	{
		for (int i = 1; i <= T; i++)
		{
			scanf("%d %d %d", &N, &L, &W);
			cnt = 0;
			for (int j = 1; j <= N; j++)
			{
				double C, R;
				scanf("%lf %lf", &C, &R);
				double x = 1.0 * sqrt((1.0 * R * R) - ((W / 2.0) * (W / 2.0))); // 勾股定理, 为什么需要这样求呢? 就留给读者自己思考了哦~ 
				if (R * 2 <= W) continue;
				++cnt;
				P[cnt].l = max((double)0, C - x); // 如果它的左端点超出了左边界0 , 则把它的左端点设置为左边界 
				P[cnt].r = min((double)L, C + x); // 如果它的右端点超出了右边界L , 则把它的右端点设置为右边界 
			}
			int ans = 0;
			double maxr = 0.0;
			bool check = 0;
			while (maxr < L)
			{
				double nowl = maxr;
				for (int j = 1; j <= cnt; j++)
					if (P[j].l <= nowl && P[j].r > maxr) maxr = P[j].r;
				if (maxr == nowl)
				{
					check = 1;
					break;
				}
				++ans;
			}
			if (check) printf("-1");
			else printf("%d", ans);
			printf("\n");
		}
	}
	return 0;
}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值