1. 程式人生 > >備戰Noip2018模擬賽15(A組)T3 Clock 淘淘起床了

備戰Noip2018模擬賽15(A組)T3 Clock 淘淘起床了

10月17日備戰Noip2018模擬賽15(A組)

T3 Clock 淘淘起床了

題目描述

今天就是ioi啦,好開心!

可是淘淘還在睡覺啊,要把他叫醒來! 

淘淘在家睡覺都是分身的,他會分身成n個人,然後在家裡找個地方朝四面牆中任意一面躺下睡覺! 

要想叫醒淘淘,必須讓每個分身都聽到鬧鐘!

但是啊,鬧鐘太多了會導致聲音過大把淘淘震傻導致不會寫FWT和FFT,那麼他就無法akioi啦!

在二維平面上以(0,0)為左下角,(w,d)為右上角的矩形區域內有一些點,每個點代表一個人,每個人將會面對一個方向(東南西北),一個人的視野範圍是以該點為頂點的直角,角平分線與其所面對的方向平行,角的兩條邊會與矩形交於兩點,矩形上兩點之間的部分,為該人的可視部分。為了讓每個人都能知道時間,你需要在矩形的邊界上放置一些時鐘(視為一個點),使得每個人的可視部分內都至少有一個時鐘,求最少需要放置幾個時鐘。

輸入格式

第一行三個整數,n,w,d;分別代表人數,以及矩形的右上角所在點的座標。 

接下來n行每行兩個整數和一個大寫字母,表示該人的座標,以及面對的方向

輸出格式

輸出一行,一個整數,代表最少需要放置幾個時鐘。

輸入樣例

2 10 6  
4 4 E  
6 4 W

輸出樣例

2

樣例解釋

如圖A

資料範圍

對於10%的資料:n≤5, w≤5, d≤5。 

對於20%的資料:n≤100, w≤100, d≤100。 

對於40%的資料:n≤1000, w≤5000, d≤5000。 

對於另外10%的資料:n≤1000, w=2, d≤105。 

對於70%的資料:n≤1000,w≤105,d≤105。

對於100%的資料:n≤5000,w≤105, d≤105。

保證人的座標不在邊界上,但不保證座標不重複。

思路

模擬 + 貪心

這道題真的超級考噁心的細節啊

先把問題簡單化, 將每個分身的視野範圍對應到區間上, 然後這個問題就變成了, 在一個環上有許多區間, 求最大不相交區間數,說起來很簡單, 但是對於如何將視野對應到區間上還是要仔細一些, 具體方法看後面;

線上段上求最大不相交區間數是很常見的, 如果到了環上, 可以把每個區間作為開始的第一個區間列舉,再使用貪心求解, 最後取最大值, 複雜度\Theta \left(n^2 \right )

再來說一些細節部分

coordinate

 首先要對環進行標號, 也就是自定義函式coordinate的作用, lft[], dwn[], rgt[], upp[]分別記錄, 左下右上思辨的座標的座標 

inline void coordinate ()
{
	for (int i = d; i >= 0; -- i){
		lft[i] = ++ co;
	}
	for (int i = 1; i < w; ++ i){
		dwn[i] = ++ co;
	}
	for (int i = 0; i <= d; ++ i){
		rgt[i] = ++ co; 
	}
	for (int i = w - 1; i >= 1; -- i){
		upp[i] = ++ co;
	}
	return ;
}

那舉個栗子說明一下, 比如一個w = 4, d = 3, 的矩形, 標記完座標後是這樣的

mark

mark函式用於把視野範圍轉換為區間

u = w - x;
v = d - y;
if (ch == 'N'){
	L = u + y <= d ? rgt[u + y] : upp[v + x];
	R = x + y <= d ? lft[x + y] + C : upp[x - v];
	a[cnt] = Interval (L, R);
	return ;
}

就用面朝北方的來舉例子

如果一個人的視野是這樣的

以右端點R為例, 若x + y\leqslant d, 說明R在lft[]上, 如上圖所示, 反之則在upp[]上, 這裡可以利用視角是45°的等腰直角三角形性質來進行轉化, 其他方向也是類似的

程式碼

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>

using namespace std;

const int MAXN = 5e3 + 5;
const int MAXE = 1e5 + 5;

int n, w, d, co, C, tot;
int lft[MAXE], dwn[MAXE], rgt[MAXE], upp[MAXE];

struct Interval{
	int l, r;
	
	Interval(){	}
	Interval (int _l, int _r) : l (_l), r (_r) { }
	
	bool operator  < (const Interval &rhs) const 
	{
		return r < rhs.r || r == rhs.r && l < rhs.l;
	}
}a[MAXN];

inline int readint ();
inline char readchar ();
inline void coordinate ();
inline void mark (int x, int y, char ch, int cnt);
inline void count ();


int main ()
{
	freopen ("clock.in", "r", stdin);
	freopen ("clock.out", "w", stdout);
	
	int x, y;
	char ch;
	n = readint (), w = readint (), d = readint ();
	
	coordinate ();
	
	C = (w + d) * 2;			//C是周長 
	for (int i = 1; i <= n; ++ i){
		x = readint (), y = readint (), ch = readchar ();
		mark (x, y, ch, i);	
	}
	
	sort (a + 1, a + 1 + n);			//按照右端點排序 
	
	count ();			//貪心求解 
	
	printf ("%d", tot);
	
	fclose (stdin);
	fclose (stdout);
	return 0;
}

inline int readint ()
{
	char ch = getchar ();
	while (!isdigit (ch)) ch = getchar ();
	int x = 0;
	while (isdigit (ch)){
		x = x * 10 + ch - '0';
		ch = getchar ();
	}
	return x;
}

inline char readchar ()
{
	char ch;
	while (ch = getchar (), ch <'A' || ch > 'Z');
	return ch;
}

inline void coordinate ()
{
	for (int i = d; i >= 0; -- i){
		lft[i] = ++ co;
	}
	for (int i = 1; i < w; ++ i){
		dwn[i] = ++ co;
	}
	for (int i = 0; i <= d; ++ i){
		rgt[i] = ++ co; 
	}
	for (int i = w - 1; i >= 1; -- i){
		upp[i] = ++ co;
	}
	return ;
}

inline void mark (int x, int y, char ch, int cnt)			//將視野範圍轉化為區間 
{
	int u, v, L, R;
	u = w - x;
	v = d - y;
	if (ch == 'N'){
		L = u + y <= d ? rgt[u + y] : upp[v + x];
		R = x + y <= d ? lft[x + y] + C : upp[x - v];
		a[cnt] = Interval (L, R);
		return ;
	}
	if (ch == 'S'){
		L = y - x >= 0 ? lft[y - x] : dwn[x - y];
		R = x + y < w ? dwn[x + y] : rgt[y - u];
		a[cnt] = Interval (L, R);
		return ;
	}
	if (ch == 'W'){
		if (x - v > 0){
			L = upp[x - v];
			R = x - y > 0 ? dwn[x - y] + C : lft[y - x] + C;
		}
		else {
			L = lft[x + y];
			R = x - y > 0 ? dwn[x - y] : lft[y - x];
		}
		a[cnt] = Interval (L, R);
		return ;
	}
	if (ch == 'E'){
		L = x + y < w ? dwn[x + y] : rgt[y - u];
		R = y + u <= d ? rgt[y + u] : upp[x + v];
		a[cnt] = Interval (L, R);
		return ;
	}
}

inline void count ()
{
	int end, cnt;
	for (int i = 1; i <= n; ++ i){
		if (a[i].r <= C){
			end = a[i].r;
			cnt = 1;
			for (int j = i + 1; j <= n; ++ j){
				if (a[j].r <= C && a[j].l > end){
					end = a[j].r;
					++ cnt;
				}
				else if (a[j].r > C && a[j].r - C < a[i].l && a[j].l > end){
					end = a[j].r;
					++ cnt;
				}
			}
			if (end < C && a[1].r < a[i].l){
				end = a[1].r;
				++ cnt;
			}
			else if (end >= C) end -= C;
			for (int j = 1; j < i; ++ j){
				if (a[j].r <= C && a[j].r < a[i].l && a[j].l > end){
					end = a[j].r;
					++ cnt;
				}
			}
			tot = max (tot, cnt);
		}
		else{
			end = a[i].r - C;
			cnt = 1;
			for (int j = 1; j <= n; ++ j){
				if (a[j].r <= C && a[j].l > end && a[j].r < a[i].l){
					end = a[j].r;
					++ cnt;
				}
			}
			tot = max (tot, cnt);
		}
	}
	return ;
}