備戰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。
保證人的座標不在邊界上,但不保證座標不重複。
思路
模擬 + 貪心
這道題真的超級考噁心的細節啊
先把問題簡單化, 將每個分身的視野範圍對應到區間上, 然後這個問題就變成了, 在一個環上有許多區間, 求最大不相交區間數,說起來很簡單, 但是對於如何將視野對應到區間上還是要仔細一些, 具體方法看後面;
線上段上求最大不相交區間數是很常見的, 如果到了環上, 可以把每個區間作為開始的第一個區間列舉,再使用貪心求解, 最後取最大值, 複雜度
再來說一些細節部分
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為例, 若, 說明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 ;
}