算法题:太阳

问题描述

在二维坐标系中,太阳的坐标为 ( s x , s y ) (sx, sy) (sx,sy),有m条平行于x轴的线段,其坐标、长度和高度为 ( x i , l i , h i ) i = 1 , 2 , . . . , m (x_i, l_i, h_i) _{i=1,2,...,m} (xi,li,hi)i=1,2,...,m,问多少条线段能够被太阳照到。输入条件 0 < = h i < s y 0<=h_i<sy 0<=hi<sy
在这里插入图片描述

解析

将所有线段从高到底排序,之后依次遍历计算其在x轴上的投影,如果与已有的投影段重合,则说明不能被太阳照到。需要特别注意投影段的更新过程,这里采用map保存阴影段的左端点和右端点横坐标,应该有更简洁的实现方法。

#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
using namespace std;

// 记录x轴上的阴影段的有序集合
static map<double, double> record;

// 保存线段的类
struct Line
{
	int xl;
	int xr;
	int y;

	Line(int x, int y_, int l) : xl(x), xr(x + l), y(y_) {}
};

bool operator<(const Line &lhs, const Line& rhs)
{
	return lhs.y < rhs.y;
}

// 保存Line的vector
static vector<Line> lines;

// 计算由(x, y)和(sx, sy)确定的点与x轴的交点的横坐标
constexpr double cal(double x, double y, double sx, double sy)
{
	return x - y * (x - sx) / (y - sy);
}

// 将新的阴影段加入集合record,并返回是否没有被阴影段覆盖
bool addLine(double xl, double xr)
{
	bool res = true;
	auto edge_left = record.upper_bound(xl);
	auto edge_right = record.upper_bound(xr);
	if (edge_left != record.begin() && xr <= (--edge_left)++->second)
		res = false;
	else
	{
		// 没有重合,则加入阴影段集合
		if (edge_left != record.begin())
		{
			--edge_left;
			if (xl <= edge_left->second) xl = edge_left->first;
			else ++edge_left;
		}
		if (edge_right != record.begin())
		{
			--edge_right;
			if (xr <= edge_right->second) xr = edge_right->second;
			++edge_right;
		}
		record.erase(edge_left, edge_right);
		record.insert({xl, xr});
	}

	return res;
}

int main()
{
	int ans = 0;

	// 读取太阳点和线段数量
	int m, sx, sy;
	cin >> m >> sx >> sy;
	while (m--)
	{
		int x, y, l;
		cin >> x >> y >> l;
		lines.emplace_back(x, y, l);
	}

	// 将线段从高到低排序
	sort(lines.rbegin(), lines.rend());

	for (auto &li : lines)
	{
		// 计算在x轴上的阴影起始位置
		double xl = cal(li.xl, li.y, sx, sy);
		double xr = cal(li.xr, li.y, sx, sy);
		if (addLine(xl, xr)) ++ans;
	}
	cout << ans << endl;
	return 0;
}
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HilariousDog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值