網易互娛2017實習生招聘在線筆試(三)

題目3 : 畫線

時間限制:10000ms
單點時限:1000ms
內存限制:256MB

描述

小王最近在開發一種新的遊戲引擎,但是最近遇到了性能瓶頸。於是他打算從最基本的畫線功能開始分析優化。畫線其實就是調用一次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");
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章