1. 程式人生 > 實用技巧 >AcWIng 趕牛入圈

AcWIng 趕牛入圈

這裡使用非常樸素的O(n3logn)+剪枝過了這題.

N的範圍才到500,然而座標的範圍讓人剛好沒法開二維陣列,<<進階>>一書上有字首和的標籤,我沒想出來如何實現.

求最小邊長,使得其中含有至少C單位三葉草,這是二分答案.

現在需要一個檢查邊長為p的正方形能否滿足條件的函式,他要對這個正方形所有可能的左上角座標進行檢查.

哪些座標是可能的?可以很清楚地看出來那些所在一整行或者一整列都沒有三葉草的點根本不需要考慮,總有更優的行或列可以選擇.

所以把輸入所有三葉草的x,y座標存下來,他們相互任意組合構成所有的可能座標.這種對應產生不多於n2個點需要檢查.

現在如何檢查已知左上角座標和邊長的正方形內有多少三葉草?與其逐個檢查正方形內的點,不如逐個檢查三葉草的座標.

與其逐個檢查三葉草的座標,不如將三葉草按座標排序後從正方形邊界座標開始檢查,如此便把每個正方形檢查複雜度降到了至多O(n).

現在還是會超時,想了一會又來了個剪枝,如果在檢查所有三葉草中途發現已經在正方形內的數量加上還沒檢查的三葉草數量已經小於c了,那麼顯然不行,直接跳到下一個正方形.

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

typedef pair<int, int> PII;
int n, c, x[510], y[510], idx, idy; PII s[510]; bool cmpX(PII a, PII b) { return a.first < b.first; } bool check(int p) { for (int i = 1; i <= idx; i++) { PII tmp(x[i], -1); int begin = lower_bound(s + 1, s + n + 1, tmp, cmpX) - s; for (int j = 1; j <= idy; j++) {
int sx = x[i], sy = y[j], ct = 0; for (int k = begin; s[k].first <= sx + p - 1 && k <= n; k++) if (s[k].second >= sy && s[k].second <= sy + p - 1) { ct++; if (ct >= c) return true; if(ct + n - k < c) break; } } } return false; } int main() { // freopen("in.txt", "r", stdin); bool bx[10010], by[10010]; memset(bx, 0, sizeof(bx)); memset(by, 0, sizeof(by)); scanf("%d%d", &c, &n); int a, b; for (int i = 1; i <= n; i++) { scanf("%d%d", &a, &b); s[i] = {a, b}; bx[a] = true; by[b] = true; } s[0] = {-1, -1}; for (int i = 1; i <= 10001; i++) { if (bx[i]) x[++idx] = i; if (by[i]) y[++idy] = i; } sort(s + 1, s + n + 1, cmpX); int l = 0, r = 10001; while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("%d\n", l); return 0; }

後來我才知道用pair去sort可以按x第一關鍵字排序,按y第二關鍵字排序,這樣就可以用lower_bound找到x和y的起點邊界,應該會再快一點.