레이블이 sweeping인 게시물을 표시합니다. 모든 게시물 표시
레이블이 sweeping인 게시물을 표시합니다. 모든 게시물 표시

2018년 7월 28일 토요일

10534 락페스티벌

10534 락페스티벌 https://www.acmicpc.net/problem/10534


직사각형 N개가 주어질 때 모서리가 인접한 직사각형은 같은 그룹이다.
가장 넓은 그룹의 넓이를 구하는 문제이다.
============================================================================




직사각형 한 개당 너비와 높이가 최대 500이므로 직사각형 각각의 grid를 이용해서 풀기는 어렵다.
직사각형의 선분을 이용해보자!

모든 직사각형의 x,y좌표들을 압축을 해주고 y좌표에는 직사각형의 너비를, x좌표에는 직사각형의 
높이를 넣어준다.

선분이 교차하는 부분이 있다면 같은 그룹이다.
선분이 교차하는것은 sweeping을 하면서 판별해 주고 union find로 같은그룹으로 묶어주면 해결된다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 5;
typedef long long ll;
struct Rect {
    int x, y, w, h;
};
int n;
bool visit[MAXN];
Rect rect[MAXN];
int par[MAXN];
ll sze[MAXN];
int find(int x) {
    if (x == par[x]) return x;
    return par[x] = find(par[x]);
}
void merge(int u, int v) {
    u = find(u); v = find(v);
    if (u == v) return;
    if (sze[u] > sze[v]) swap(u, v);
    sze[v] += sze[u];
    sze[u] = 0;
    par[u] = v;
}
vector<ll> xpos, ypos;
set<pair<ll,pair<ll, int>>> savex[100005], savey[100005];
int main() {
    scanf("%d"&n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d"&rect[i].x, &rect[i].y, &rect[i].w, &rect[i].h);
        sze[i] = rect[i].w*rect[i].h;
        par[i] = i;
        xpos.push_back(rect[i].x);
        xpos.push_back((ll)rect[i].x + (ll)rect[i].w);
        ypos.push_back(rect[i].y);
        ypos.push_back((ll)rect[i].y + (ll)rect[i].h);
    }
    sort(xpos.begin(), xpos.end());
    sort(ypos.begin(), ypos.end());
    xpos.erase(unique(xpos.begin(), xpos.end()), xpos.end());
    ypos.erase(unique(ypos.begin(), ypos.end()), ypos.end());
    for (int i = 1; i <= n; i++) {
        int idx = lower_bound(xpos.begin(), xpos.end(), rect[i].x) - xpos.begin();
        savex[idx].insert({ rect[i].y, {-rect[i].h, i} });
        idx = lower_bound(xpos.begin(), xpos.end(), (ll)rect[i].x + (ll)rect[i].w) - xpos.begin();
        savex[idx].insert({ rect[i].y, {-rect[i].h, i} });
        idx = lower_bound(ypos.begin(), ypos.end(), rect[i].y) - ypos.begin();
        savey[idx].insert({ rect[i].x, {-rect[i].w, i} });
        idx = lower_bound(ypos.begin(), ypos.end(), (ll)rect[i].y + (ll)rect[i].h) - ypos.begin();
        savey[idx].insert({ rect[i].x, {-rect[i].w ,i} });
    }
    for (int i = 0; i < xpos.size(); i++) {
        auto curr = savex[i].begin();
        auto nxt = curr;
        ll cover = (*curr).first - (*curr).second.first;
        while (++nxt != savex[i].end()) {
            int idx = (*curr).second.second, xdi = (*nxt).second.second;
            if ((*nxt).first <= cover) {
                merge(idx, xdi);
            }
            cover = max(cover, (*nxt).first - (*nxt).second.first);
            curr = nxt;
        }
    }
    for (int i = 0; i < ypos.size(); i++) {
        auto curr = savey[i].begin();
        auto nxt = curr;
        ll cover = (*curr).first - (*curr).second.first;
        while (++nxt != savey[i].end()) {
            int idx = (*curr).second.second, xdi = (*nxt).second.second;
            if ((*nxt).first <= cover) {
                merge(idx, xdi);
            }
            cover = max(cover, (*nxt).first - (*nxt).second.first);
            curr = nxt;
        }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) ans = max(ans, sze[find(i)]);
    printf("%lld\n", ans);
    return 0;
}

cs

2018년 1월 4일 목요일

14603 소금과 후추(Large)

14603 소금과 후추(Large) https://www.acmicpc.net/problem/14603

행렬이 주어질 때 그 행렬을 $w$X$w$마다 압축하는 문제이다.
압축한다는것은 $w$X$w$행렬에서 중앙값을 추출한다는 것이다.

naive하게 생각하면 $O(nmw^{2})$이라 당연하게 시간초과가 발생할 것이다.

segment treeplane sweeping을 이용하면 $O(nmw$ $lg(k))$만에 해결할 수 있다.
($k$는 여기서 segment tree의 크기)

$n,m,w$가 각각 5,6,3이라면 위와 같이 훑어서 전체값을 구할 수 있다.
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
int N, M, L, K;
int arr[301][301];
vector<vector<int>> ans;
struct Segment {
    vector<int> tree;
    int size;
    Segment() {}
    Segment(int N) :size(N + 1) { tree.resize(4 * (N + 1), 0); }
    int update(int idx, int val, int node, int nl, int nr) {
        if (idx < nl || idx > nr) return tree[node];
        if (nl == nr) return tree[node] += val;
        int mid = (nl + nr) >> 1;
        return tree[node] = update(idx, val, node * 2, nl, mid) + update(idx, val, node * 2 + 1, mid + 1, nr);
    }
    void update(int idx, int val) {
        update(idx, val, 10size - 1);
    }
    int query(int val, int node, int nl, int nr) {
        if (nl == nr) return nl;
        int mid = (nl + nr) >> 1;
        if (tree[node * 2>= val) return query(val, node * 2, nl, mid);
        else return query(val - tree[node * 2], node * 2 + 1, mid + 1, nr);
    }
    int query(int val) {
        return query(val, 10size - 1);
    }
};
int main() {
    scanf("%d%d%d%d"&N, &M, &L, &K);
    Segment seg(L);
    for (int n = 0;n < N;n++for (int m = 0;m < M;m++scanf("%d"&arr[n][m]);
    for (int n = 0;n < K;n++for (int m = 0;m < K;m++)seg.update(arr[n][m], 1);
    for (int n = 0;n < N - K + 1;n++) {
        if (!(n & 1)) {        // ->
            vector<int> tmp;
            if (n != 0)
                for (int m = 0;m < K;m++) seg.update(arr[n - 1][m], -1), seg.update(arr[n + K - 1][m], 1);
            for (int m = 0;m < M - K + 1;m++) {
                if (m == 0) {}
                else {
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m - 1], -1);
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m + K - 1], 1);
                }
                tmp.push_back(seg.query((K*+ 1/ 2));
            }
            ans.push_back(tmp);
        }
        else {            // <-
            vector<int> tmp;
            for (int m = M - K;m < M;m++) seg.update(arr[n - 1][m], -1), seg.update(arr[n + K - 1][m], 1);
            for (int m = M - K;m >= 0;m--) {
                if (m == M - K) {}
                else {
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m + K], -1);
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m], 1);
                }
                tmp.push_back(seg.query((K*+ 1/ 2));
            }
            reverse(tmp.begin(), tmp.end());
            ans.push_back(tmp);
        }
    }
    for (auto &n : ans) {
        for (auto &m : n) printf("%d ", m);
        printf("\n");
    }
    return 0;
}
cs

푸니까 1초제한에서 888ms가 나와서 조금 찝찝하긴 하지만 다른분들 풀이랑 별 차이가 없는듯 하다.

2017년 11월 12일 일요일

2104 부분배열 고르기, 12846 무서운 아르바이트

2104 부분배열 고르기 https://www.acmicpc.net/problem/2104
12846 무서운 아르바이트 https://www.acmicpc.net/problem/12846

$(\sum_{i}^{N} A_{i})*(min_{i\in{N}} A_{i})$ 최댓값을 구하는 문제다.
따지고보면 둘다 같은 문제이다.

여러가지 방법으로 풀 수 있는데 나는 plane sweeping 방법으로 접근했다.
문제의 입력을 예시로 들겠다.
(사실 위의 그림을 보면 알겠지만 유명한 히스토그램 문제이다.)
여기서 높이가 높은 순으로 최대한 양쪽으로 퍼질 수 있는만큼 퍼지는 방식으로 구했다.

6과 5는 양쪽으로 퍼질 수 없지만 4는 양쪽 하나씩 퍼질 수 있다.
이것을 bucket(묶음)처럼 저장하여 이미 묶여져있는 bucket을 만났을 때 맨 끝쪽으로 이동시키도록
만들었다.

stack을 이용해 $O(N)$만에 해결하는 방식도 이와 유사하지만 이 방법에서는 stack을 쓰지 않고
정렬한다는 차이가있다.
#include <cstdio>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Bucket { int l, r; };
int N;
int arr[100001];
long long sum[100001];
pair<intint> save[1000001];
vector<pair<int,int>> v;
bool visit[100001];
Bucket bucket[100001];
long long ans = 0;
void update(Bucket &buck, int l, int r) {
    buck.l = l;
    buck.r = r;
}
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf("%d"&arr[n]), v.push_back({ arr[n],n });
    for (int n = 0;n < N;n++) {
        sum[n] += (long long)arr[n] + (n == 0 ? 0 : sum[n - 1]);
    }
    sort(v.begin(), v.end(), greater<pair<intint>>());
    for (int n = 0;n < N;++n) {
        int here = v[n].first;
        int idx = v[n].second;
        int l = idx, r = idx;
        if (visit[idx]) continue;
        visit[idx] = true;
        while (l - 1 >= 0 && (arr[l - 1>= here || visit[l - 1])) {
            if (visit[l - 1]) l = bucket[l - 1].l;
            else visit[--l] = true;
        }
        while (r + 1 < N && (arr[r + 1>= here || visit[r + 1])) {
            if (visit[r + 1]) r = bucket[r + 1].r;
            else visit[++r] = true;
        }
        update(bucket[l], l, r);
        update(bucket[r], l, r);
        ans = max(ans, (sum[r] - (l == 0 ? 0 : sum[l - 1])) * (long long)here);
    }
    printf("%lld\n", ans);
    return 0;
}
cs

위에서 언급했듯이 히스토그램문제처럼 divide & conquer로 풀 수 있다.
#include <cstdio>
typedef long long ll;
inline ll min(ll a, ll b) { return a > b ? b : a; }
inline ll max(ll a, ll b) { return a < b ? b : a; }
int N;
ll arr[100001];
ll sum[100001];
ll solve(int l, int r) {
    if (l == r) return arr[l] * arr[l];
    int mid = (l + r) >> 1;
    ll ret = max(solve(l, mid), solve(mid + 1, r));
    int left = mid, right = mid + 1;
    ll Min = min(arr[left], arr[right]);
    ret = max(ret, (arr[left] + arr[right]) * Min);
    while (l < left || right < r) {
        if (l == left || (right < r && arr[right + 1> arr[left - 1])) right++, Min = min(Min, arr[right]);
        else left--, Min = min(Min, arr[left]);
        ret = max(ret, (sum[right] - (left == 0 ? 0 : sum[left - 1]))*Min);
    }
    return ret;
}
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf("%lld"&arr[n]);
    for (int n = 0;n < N;n++) sum[n] += arr[n] + (n == 0 ? 0 : sum[n - 1]);
    printf("%lld\n", solve(0, N - 1));
    return 0;
}
cs


2017년 8월 15일 화요일

Minima/maxima over all fixed-size arrays (multi-dimensional)

http://codeforces.com/blog/entry/53810

$N\cdot M$크기의 2차원 배열이 있다고 생각해 보자.
이 배열의 $A\cdot B$크기의 부분배열의 최솟값/최댓값을 구하는 방법이다.

바로 solution을 말하자면 1차원 배열일때의 solution을 2차원으로 확장 시키는 것이다.

1차원일 경우에는 sweeping방법으로 훑으면서 greedy하게 답을 구해낼 수 있다.
$i < j, A[i] >= A[j]$이면 $A[i]$를 삭제하면 된다.
(물론 원하는 부분배열의 크기보다 크거나 같은 경우일때 삭제시킨다.)

2차원일때는 위의 방법을 가지고 y축으로 한번 해서 압축, x축으로 해서 압축하면 나온다.
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
std::multiset<int> save;
int N, M, A, B;
int map[1001][1001];
int tmp[1001][1001], ret[1001][1001];
int main() {
    scanf("%d%d%d%d"&N, &M, &A, &B);
    for (int n = 1;n <= N;n++)
        for (int m = 1;m <= M;m++)
            scanf("%d"&map[n][m]);
    //세로 압축
    for (int m = 1;m <= M;m++) {
        save.clear();
        // 일단 크기가 A되기전 까지 집어넣음
        for (int n = 1;n < A;n++) save.insert(-map[n][m]);    
        for (int n = A;n <= N;n++) {
            save.insert(-map[n][m]);
            // 크기가 A가 넘으면 앞에 들어왔던것 지움
            if (save.size() > A) save.erase(save.find(-map[n - A][m]));
            tmp[n - A + 1][m] = -*save.begin();
        }
    }
    //가로 압축
    for (int n = 1;n <= N;n++) {
        save.clear();
        //일단 크기가 B되기전 까지 집어넣음
        for (int m = 1;m < B;m++) save.insert(-tmp[n][m]);
        for (int m = B;m <= M;m++) {
            save.insert(-tmp[n][m]);
            //크기가 B가 넘으면 앞에 들어왔던것 지움
            if (save.size() > B) save.erase(save.find(-tmp[n][m - B]));
            ret[n][m - B + 1= -*save.begin();
        }
    }
    printf("\n압축 후\n");
    for (int n = 1;n <= N - A + 1;n++) {
        for (int m = 1;m <= M - B + 1;m++)
            printf("%d ", ret[n][m]);
        printf("\n");
    }
    return 0;
}
cs
위의 소스는 2차원 배열일때 $O(N\cdot M)$복잡도로 maximum을 구하는 방법이다.
minimum을 구하고 싶으면 multiset에 -붙은것을 다 없애면 된다.