레이블이 백준 알고리즘인 게시물을 표시합니다. 모든 게시물 표시
레이블이 백준 알고리즘인 게시물을 표시합니다. 모든 게시물 표시

2018년 9월 7일 금요일

2285 우체국

2285 우체국 https://www.acmicpc.net/problem/2285

N개의 마을은 각각 $X_{i}$에 위치하고 $A_{i}$의 사람들이 살고 있다.
우체국을 하나 세울 때 각 사람들 까지의 거리가 최소가 되는 위치를 구하는 문제이다.

처음엔 삼진검색을 생각했으나 $O(N)$ 알고리즘이 떠올랐다.

이 문제에서 우체국은 N개의 마을 중 한 곳에 세워줘야 최적이다.
$a$와 $b$사이가 $d$인 두 마을만 있는 경우를 예시로 들어보겠다.

$a<=x<=b$인 $x$가 존재한다고 가정했을 때 우리가 구하고자하는 값은 $A(x-a) + B(b-x)$이다.
$(A-B)x-Aa+Bb$의 값을 최소화하는것이 우리의 목표인데 
$-Aa+Bb$는 상수이므로 $x$차수항만 생각해 본다면 
$A-B>0$인 경우에는 절대값을 낮춰야 하므로 $x=a$가 되어야하고
$A-B<0$인 경우에는 절대값을 키워야 하므로 $x=b$가 되어야 한다.
따라서 두 위치중 한 곳이 최적이다.
마을이 3개 이상인것도 확장시켜 생각해보면 증명이 가능하다.

현재 마을에 우체국을 세운다고 생각했을 때 마을 기준으로 오른쪽과 왼쪽에 있는 사람들의 수와 
거리를 잘 관리하면 해결할 수 있다.
__int128을 사용하여 통과하였다 ㅋㅋ;

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int n;
pair<ll, ll> arr[MAXN];
using Integer128 = __int128;
std::ostream& operator<<(std::ostream& dest, const Integer128& value)
{
    Integer128 tmp = value < 0 ? -value : value;
    std::array<char128> buffer;
    auto d = buffer.begin();
    while (tmp != 0) {
        *d++ = "0123456789"[tmp % 10];
        tmp /= 10;
    }
    if (value < 0) {
        *d++ = '-';
    }
    std::ostream_iterator<char> out_it(dest);
    std::reverse_copy(buffer.begin(), d, out_it);
    return dest;
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++cin >> arr[i].first >> arr[i].second;
    sort(arr, arr + n);
 
    __int128 fr = arr[0].second, en = 0;
    __int128 dist = 0;
    for (int i = 0; i < n; i++) {
        if(i) en += (__int128)arr[i].second;
        dist += (__int128)(arr[i].first - arr[0].first) * arr[i].second;
    }
    __int128 mx = dist;
    __int128 ans = arr[0].first;
    __int128 prv = 0;
 
    for (int i = 1; i < n; i++) {
        dist -= (__int128)(arr[i].first - arr[i - 1].first) * en;
        dist += (__int128)(arr[i].first - arr[i - 1].first) * fr;
        en -= (__int128)arr[i].second;
        fr += (__int128)arr[i].second;
        if (mx > dist) {
            mx = dist;
            ans = arr[i].first;
        }
        else if (mx == dist) {
            if (ans > arr[i].first) ans = arr[i].first;
        }
    }
    cout << ans;
    return 0;
}
cs

사실 더 간단한 풀이가 있다.
사람 수의 절반정도의 위치한 마을이 답이된다.

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
typedef long long ll;
int n;
pair<intint> arr[MAXN];
int main() {
    scanf("%d"&n);
    ll sum = 0, s = 0;
    for (int i = 0; i < n; i++scanf("%d%d"&arr[i].first, &arr[i].second), sum += arr[i].second;
    sort(arr, arr + n);
    for (int i = 0; i < n; i++) {
        s += arr[i].second;
        if (s * 2ll >= sum) {
            printf("%d\n", arr[i].first);
            break;
        }
    }
    return 0;
}
cs

2018년 3월 23일 금요일

10323 Excellent Engineers

10323 Excellent Engineers https://www.acmicpc.net/problem/10323
2336 굉장한 학생 https://www.acmicpc.net/problem/2336

각 사람마다 communication skills, programming skills, algorithmic knowledge가 등수로 주어진다.
어떤 사람의 세 가지 등수보다 모두 높은 등수를 가진 사람이 존재하면 그사람을 추천해서는 안된다.
사람들의 등수가 주어질 때 추천할 수 있는 사람의 수를 구하는 문제이다.

세 등 수중 앞의 등수를 기준으로 정렬하면 차원 하나를 줄일 수 있다.
그 후에 segment tree로 관리를 해주면서 값을 갱신해나가면 된다.
#include <bits/stdc++.h>
using namespace std;
#define INF 987654321
const int MAXN = 1e5 + 5;
struct Save { int a, b, c; };
int t, n;
Save save[MAXN];
struct Segment {
    int sze;
    vector<int> tree;
    Segment() {}
    Segment(int s) :sze(s) { tree.resize(4 * s, INF); }
    int update(int idx, int val, int node, int nl, int nr) {
        if (nr < idx || nl > idx) return tree[node];
        if (nl == nr) return tree[node] = val;
        int mid = (nl + nr) >> 1;
        return tree[node] = min(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, 10, sze - 1);
    }
    int query(int l, int r, int node, int nl, int nr) {
        if (r < nl || l > nr) return INF;
        if (l <= nl && nr <= r) return tree[node];
        int mid = (nl + nr) >> 1;
        return min(query(l, r, node * 2, nl, mid), query(l, r, node * 2 + 1, mid + 1, nr));
    }
    int query(int l, int r) {
        return query(l, r, 10, sze - 1);
    }
};
int main() {
    scanf("%d",&t);
    while (t--) {
        scanf("%d"&n);
        for (int i = 0; i < n; i++scanf("%d%d%d"&save[i].a, &save[i].b, &save[i].c);
        sort(save, save + n, [](Save &a, Save &b)->bool {
            return a.a < b.a;
        });
        Segment seg(n + 1);
        int ans = n;
        for (int i = 0; i < n; i++) {
            int mn = seg.query(0, save[i].b - 1);
            if (mn < save[i].c) ans--;
            seg.update(save[i].b, save[i].c);
        }
        printf("%d\n", ans);
    }
    return 0;
}
cs

2018년 3월 4일 일요일

11046 팰린드롬??

11046 팰린드롬?? https://www.acmicpc.net/problem/11046

자연수 N개가 주어진다.
M개의 쿼리마다 $s$부터$e$구간 까지 팰린드롬을 이루는지 여부를 판단하는 문제이다.

Manacher's algorithm으로 전처리하여 각 쿼리마다 $O(1)$에 처리할 수 있다.
짝수의 팰린드롬도 처리하기 위해 시작,끝,중간마다 $-1$을 집어넣었다.

const int MAXN = 1e6 + 6;
int arr[MAXN * 2 + 6];
cs
이 문제를 20번 넘게 정도 제출했는데 위와 같이 const 변수에 곱셈을 적용시키면 안된다ㅠㅠ

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 6;
int N, M;
int arr[MAXN];
int A[MAXN];
int main() {
    scanf("%d"&N);
    arr[0= -1;
    for (int n = 0; n < N; n++) {
        scanf("%d"&arr[2*+ 1]);
        arr[2 * n + 2= -1;
    }
    N = 2 * N + 1;
 
    int r = 0, p = 0;
    for (int n = 0; n < N; n++) {
        if (n <= r) A[n] = min(A[2 * p - n], r - n);
        while (n - A[n] - 1 >= 0 && n + A[n] + 1 < N && arr[n - A[n] - 1== arr[n + A[n] + 1]) A[n]++;
        if (r < n + A[n]) r = n + A[n], p = n;
    }
 
    scanf("%d"&M);
    for (int m = 0; m < M; m++) {
        int s, e;
        scanf("%d%d"&s, &e);
        int len = e - s + 1;
        s--; s *= 2; s++;
        e--; e *= 2; e++;
        int mid = (s + e) >> 1;
        printf("%d\n", A[mid] >= len ? 1 : 0);
    }
    return 0;
}
cs

2018년 1월 28일 일요일

12764 싸지방에 간 준하

12764 싸지방에 간 준하 https://www.acmicpc.net/problem/12764

사용자의 컴퓨터 시작 시간과 끝 시간이 주어진다.
사용자는 비어있는 자리중 가장 작은 번호부터 사용한다.
컴퓨터를 최소한으로 사용할 때 그 개수와 자리별 사용회수를 구하는 문제이다.

우선순위 큐로 해결 할 수 있다.
사용자를 시작시간 순으로 정렬시킨 후 우선순위 큐에서 종료되어 나오는 사용자들을 관리해 준다.
set으로 종료되어 나오는 사용자들의 자리 번호를 관리해주면 된다.

#include <cstdio>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
int N;
pair<intint> p[100001];
set<int> save;
int ans[100001];
int solve() {
    priority_queue<pair<intint>> pq;
    int size = 0;
    for (int n = 0; n < N; n++) {
        while (!pq.empty()) {
            if (-pq.top().first <= p[n].first) {
                save.insert(pq.top().second);
                pq.pop();
            }
            else break;
        }
        if (save.empty()) {
            pq.push({ -p[n].second, size });
            ans[size++]++;
        }
        else {
            auto idx = save.begin();
            pq.push({ -p[n].second, *idx });
            ans[*idx]++;
            save.erase(idx);
        }
    }
    return size;
}
int main() {
    scanf("%d"&N);
    for (int n = 0; n < N; n++scanf("%d%d"&p[n].first, &p[n].second);
    sort(p, p + N);
 
    int M = solve();
    printf("%d\n", M);
    for (int m = 0; m < M; m++printf("%d ", ans[m]);
    return 0;
}
cs


2018년 1월 21일 일요일

3111 검열

3111 검열 https://www.acmicpc.net/problem/3111

  1. T에 A가 없으면 알고리즘을 종료한다.
  2. T에서 처음 등장하는 A를 찾은 뒤, 삭제한다.
  3. T에 A가 없으면 알고리즘을 종료한다.
  4. T에서 마지막으로 등장하는 A를 찾은 뒤, 삭제한다.
  5. 1번으로 돌아간다.
문자열 T와 단어A가 주어질 때 위의 알고리즘으로 남아있는 문자열을 구하는 문제이다.

문자열 폭발 문제와 유사하지만 맨앞과 맨뒤의 단어를 번갈아가며 지운다는 점이 다르다. 

문자열 폭발은 실시간으로 폭발이 일어날 경우가 생기면 바로바로 지워주는 반면에
이 문제는 먼저 전처리로 폭발이 일어나는 과정을 처리하며 남는것은 우선 스택2개에 각각 넣어준다.

예를들어 단어 A가 abc이고 T가 abcaabcbc가 있다고 하면 다음과 같은 과정이 될것이다.

전처리 이후의 두 스택을 이용해서 잘 해결하면 된다.
스택을 이용한다는 것보다는 전처리 이후에 처리한다는 구상을 떠올리기 어려운 문제였다.
#include <cstdio>
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
template<typename T>
struct stack {
    T *arr;
    int ptr;
    stack() :ptr(0) { arr = new T[300055]; }
    ~stack() { delete arr; }
    void clear() {
        ptr = 0;
    }
    void push(T data) {
        arr[ptr++= data;
    }
    void pop() {
        ptr--;
    }
    T top() {
        return arr[ptr - 1];
    }
    bool empty() {
        return ptr == 0;
    }
    int size() {
        return ptr;
    }
};
int N, M;
char cmp[26];
char revcmp[26];
char str[300001];
int main() {
    scanf("%s"&cmp);
    scanf("%s"&str);
    for (M = 0; cmp[M]; M++);
    for (N = 0; str[N]; N++);
    for (int m = 0; m < M; m++) revcmp[m] = cmp[M - m - 1];
 
    stack<char> stk1, stk2;
    int l = 0, r = N - 1;
    int dir = 1;
    while (l <= r && l < N && r >= 0) {
        bool find = true;
        if (dir) {
            stk1.push(str[l]);
            if (stk1.size() >= M) {
                for (int m = stk1.size() - M, t = 0; m < stk1.size(); m++, t++) {
                    if (cmp[t] != stk1.arr[m]) {
                        find = false;
                        break;
                    }
                }
                if (find) {
                    for (int m = 0; m < M; m++) stk1.pop();
                    dir ^= 1;
                }
            }
            l++;
        }
        else {
            stk2.push(str[r]);
            if (stk2.size() >= M) {
                for (int m = stk2.size() - M, t = 0; m < stk2.size(); m++, t++) {
                    if (revcmp[t] != stk2.arr[m]) {
                        find = false;
                        break;
                    }
                }
                if (find) {
                    for (int m = 0; m < M; m++) stk2.pop();
                    dir ^= 1;
                }
            }
            r--;
        }
    }
    if (stk1.empty() && !stk2.empty()) {
        for (int m = stk2.size() - 1; m >= 0; m--printf("%c", stk2.arr[m]);
        return 0;
    }
    if(!stk1.empty() && stk2.empty()) {
        for (int m = 0; m < stk1.size(); m++printf("%c", stk1.arr[m]);
        return 0;
    }
 
    bool find = true;
    while (find) {
        if (stk1.size() + stk2.size() < M) break;
        find = false;
        for (int n = max(0, stk1.size() - M); n < stk1.size(); n++) {
            int idx = 0, l = 0, r = 0;
            for (int m = n; m < stk1.size(); m++, l++) {
                if (stk1.arr[m] == cmp[idx]) idx++;
            }
            for (int m = stk2.size() - 1; m >= 0 && l + r < M; m--, r++) {
                if (stk2.arr[m] == cmp[idx]) idx++;
            }
            if (idx == M && l + r == idx) {
                find = true;
                for (int m = 0; m < l; m++) stk1.pop();
                for (int m = 0; m < r; m++) stk2.pop();
                break;
            }
        }
    }
    for (int m = 0; m < stk1.size(); m++printf("%c", stk1.arr[m]);
    for (int m = stk2.size() - 1; m >= 0; m--printf("%c", stk2.arr[m]);
    return 0;
}
cs