题目3 : 画线
描述
小王最近在开发一种新的游戏引擎,但是最近遇到了性能瓶颈。于是他打算从最基本的画线功能开始分析优化。画线其实就是调用一次drawline命令,根据给出的两端座标,在屏幕画出对应的线段。但是小王发现,很多的drawline其实可以合并在一起,譬如下图中的线段(2,3)-(4,5)和线段(3,4)-(6,7),其实可以合并为一次drawline命令,直接画出线段(2,3)-(6,7)。当然有些线段是无法合并的,如线段(-3,8)-(1,8)和线段(3,8)-(6,8),就必须调用两次drawline命令。
画线示意图。注意颜色只是用于区分,实际线段都是黑色
给出N条drawline指令以及对应的线段座标,小王想知道,实际最少用多少次drawline指令就可以画出来。
小王想先从最简单的情况开始分析优化,所以线段只包含四种情况:水平线段,垂直线段以及正反45度的线段。
输入
每个输入数据包含多个测试点。
第一行为测试点的个数 S ≤ 10。之后是 S 个测试点的数据。
每个测试点的第一行为 N(N ≤ 105)。之后是 N 行,每行包含4个整数:x0, y0, x1, y1,表示线段(x0,y0)-(x1,y1),座标的范围在[-108, 108],保证线段的长度大于0。
输出
对于每个测试点,对应的结果输出一行,表示最少用多少次指令即可完成所有的画线。
2 4 3 8 6 8 -3 8 1 8 2 3 4 5 3 4 6 7 5 1 1 2 2 2 2 3 3 3 3 4 2 4 2 5 1 1 0 100 0样例输出
3 3
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <set>
#include <cassert>
#include <time.h>
#include <queue>
#include <map>
#include <stack>
#include <bitset>
#include <string>
using namespace std;
//=================================================================
const int maxn = 100000 + 5;
int n;
struct Point
{
int x, y;
Point (int _x = 0, int _y = 0)
:x(_x), y(_y) { }
bool operator < (const Point & b) const //在右边或位于正上方
{
return x < b.x || (x == b.x && y < b.y);
}
};
typedef pair<Point, Point> Segment; //一对Point
vector<Segment> segments[4];
int judgeSegment(const Point & a, const Point & b)
{
if (a.y == b.y) return 0; //水平
else if (a.x == b.x) return 1; //垂直
else if (a.y < b.y) return 2; //正45
else return 3; //反45
}
void input()
{
scanf("%d", &n);
for (int i = 0; i < 4; ++i) segments[i].clear();
for (int i = 0; i < n; ++i) { //N行,N个线段
Point a, b;
scanf("%d%d", &a.x, &a.y);
scanf("%d%d", &b.x, &b.y);
if (a.x > b.x || (a.x == b.x && a.y > b.y)) swap(a, b); //使b始终在a的右边和上边
int type = judgeSegment(a, b);
segments[type].push_back(make_pair(a, b)); //按线段类型分4类
}
}
int countSegment(vector<pair<int, int> > lrPairs)
{
int res = 0;
sort(lrPairs.begin(), lrPairs.end()); //先按第一位,再按第二位,升序
int pre = -1e9;
for (auto lrPair : lrPairs) {
if (lrPair.first > pre) ++res; //后一个数的x是否大于前面的最大x
pre = max(pre, lrPair.second);
}
return res;
}
int countXie(vector<Segment> xie)
{
map<int, vector<pair<int, int> > > div;//Map中的元素是自动按key升序排序
for (auto seg : xie) {
int C = seg.first.y - seg.first.x; //即直线y = kx+b中的b,k = +/- 1。 k相等时,b相等的话在同一条直线上
div[C].push_back(make_pair(seg.first.x, seg.second.x));
}
int res = 0;
for (const auto & lrPairs : div) { //针对每条直线,判断重合的线段
res += countSegment(lrPairs.second);
}
return res;
}
int countHorizontal(vector<Segment> horizontal) //水平
{
map<int, vector<pair<int, int> > > div; //按y
for (auto seg : horizontal) { //循环
int y = seg.first.y;
div[y].push_back(make_pair(seg.first.x, seg.second.x)); //y相同的全部存在div对应键值的vector中
}
int res = 0;
for (const auto & lrPairs : div) {
res += countSegment(lrPairs.second);
}
return res;
}
void solve()
{
int ans = 0;
ans += countHorizontal(segments[0]); //水平
for (auto seg : segments[1]) { //交换y和x之后,可以调用水平的统计函数
swap(seg.first.x, seg.first.y);
swap(seg.second.x, seg.second.y);
}
ans += countHorizontal(segments[1]); //垂直
ans += countXie(segments[2]); //正45°
for (auto & seg : segments[3]) { //负45°
seg.first.x = -seg.first.x; //将方向反转
seg.second.x = -seg.second.x;
if (seg.first.x > seg.second.x) swap(seg.first, seg.second);
}
ans += countXie(segments[3]);
printf("%d\n", ans);
}
int main()
{
int T; cin >> T;
while (T--) {
input();
solve();
//puts("");
}
//system("pause");
}