UVA10382 - Watering Grass 题解

原题链接

解决题目

先通过勾股定理求出每个圆向左向右实际能覆盖的距离 k k k,将题目中的圆转化为矩形。

可以看出来这是一道贪心问题。我这里使用的方法是从右往左覆盖草坪,并用 l a s t last last 标记当前覆盖到的位置。

每次循环中从左向右枚举右侧( e d ed ed)能到达当前位置 l a s t last last 的圆圈。标记出现的第一个符合要求的圆圈, l a s t last last 跳到该圆圈的左侧( s t st st)。因为这些圆圈已经按照左侧( s t st st)由大到小的顺序排序,因此枚举出来的一定是符合要求的最靠左侧的圆圈,覆盖草坪的速度最快,且不用考虑对之后选择的影响,因此一定最优。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

const int N = 10000;

struct node {
	double st;
	double ed;
	bool operator < (const node &K) const {
		return st < K.st;
    	//排序时 st 小的在前
	}
}a[N + 5];

int n, cnt, l, w;
double ww;

int main() {
	while (~scanf("%d%d%d", &n, &l, &w)) {
		ww = double(w) / 2;
		cnt = 0;
		for (int i = 1; i <= n; i++) {
			double x, y, k;
			scanf("%lf%lf", &x, &y);
			if (y <= ww) {
        		//如果连宽度也不能覆盖则跳过
				continue;
			}
			cnt++;
			k = sqrt(y * y - ww * ww);
        	//计算向左向右实际覆盖距离
			a[cnt].st = x - k;
			a[cnt].ed = x + k;
		}
		n = cnt;
		sort(a + 1, a + n + 1);
		double last = l;
		int lastp = n + 1;
		int ans = 0;
		bool flag;
		while (last > 0) {
			flag = false;
			for (int i = 1; i < lastp; i++) {
        		//只循环到上一次符合要求的圆圈所在位置
				if (a[i].ed >= last && a[i].st < last) {
					flag = true;
					last = a[i].st;
					lastp = i;
					ans++;
					break;
				}
			}
			if (!flag) {
        		//没有找到符合要求的圆圈,无法完成浇灌
				printf("-1\n");
				break;
			}
		}
		if (flag) {
			printf("%d\n", ans);
		}
	}
	return 0;
}

注意事项

  1. 题目是多组数据,组数不指定。我这里使用 while (~scanf("%d%d%d", &n, &l, &w)),当读到文件结尾时终止循环;
  2. 第 28 行,如果连宽度都不能覆盖则跳过;我刚开始时没有跳过,导致之后循环时加上了这些圆圈。会导致除样例外全错;
  3. 第 46 行我用了一个 l a s t p lastp lastp 变量,目的是让循环时只循环到上一次符合要求的圆圈,减少无法复制时的循环次数;
  4. 题目是多组数据,因此 c n t cnt cnt 等变量需要初始化,避免影响这次循环。
  5. LibreOJ 或一本通中的题目稍有不同,数据组数确定为 T T T。每次循环减去一并判断即可:
int T;
scanf("%d", &T);
while (T--) {
	...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值