題目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");
}