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

2018년 6월 6일 수요일

15708 미네크래프트

15708 미네크래프트 https://www.acmicpc.net/problem/15708
Codeforces #470 (Div.2) C - Producing Snow http://codeforces.com/contest/948/problem/C
14452 Cow Dance Show https://www.acmicpc.net/problem/14452
1781 컵라면 https://www.acmicpc.net/problem/1781   (https://lyzqm.blogspot.com/2017/10/1781.html)

pqgreedy를 이용한 문제들을 소개하고자 한다.

1. 미네크래프트
시간 TN개의 바위를 각각 캐는데 걸리는 시간이 주어지고 옆의 바위로 건너가는 시간P가 주어진다.
캘 수 있는 바위의 최대개수를 구하는 문제이다.

현재 보고있는 바위를 캘지 정하는 것으로 풀지 말고 
지금 까지 봤던 바위들 중 작은 바위들을 캐서 현재 보고있는 바위까지 건너올 수 있게끔 풀어야 한다.

이러한 유형의 문제들은 우선적으로 pq에 넣어서 처리해주는 편이 좋다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, t, p;
int arr[MAXN];
priority_queue<int> pq;
int main() {
    scanf("%d%d%d"&n, &t, &p);
    for (int i = 0; i < n; i++scanf("%d"&arr[i]);
    int sum = 0, sze = 0, ans = 0;
    for (int i = 0; i < n; i++) {
        if (t <= p * i) break;
        sze++;
        sum += arr[i];
        pq.push(arr[i]);
        while (!pq.empty() && sum > t) {
            sum -= pq.top();
            sze--;
            pq.pop();
        }
        if (sum > t) break;
        ans = max(ans, sze);
        sum += p;
    }
    printf("%d\n", ans);
    return 0;
}
cs

2. Producing Snow 
N일 동안 눈이 $s_{i}$만큼 쌓인다.
각각의 날에 기온은 $t_{i}$이다.
눈이 녹는 정도는 그날 기온에 영향을 받으며 이전에 남았던 눈 또한 녹는다.
각각의 날에 녹는 눈의 양을 구하는 문제이다.


대회때는 못풀었던 아쉬운 문제이다.
pq에 $s_{i}$와 이전에 기온들의 합을 넣어 관리할 수 있다.
눈이 완전히 녹는것은 이전 기온들의 합과 현재 기온의 합보다 작거나 같아지는 경우이다.
아직 pq에 남아있는 크기만큼 눈들 또한 더 녹기 때문에 이것도 더해줘야한다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
const int MAXN = 1e5 + 5;
int arr[MAXN];
priority_queue<ll> pq;
int main() {
    scanf("%d"&n);
    for (int i = 0; i < n; i++scanf("%d"&arr[i]);
    ll s = 0;
    for (int i = 0, minus; i < n; i++) {
        ll ret = 0;
        scanf("%d"&minus);
        pq.push(-(arr[i] + s));
        s += minus;
        while (!pq.empty() && -pq.top() <= s) {
            ret += -pq.top() - s + minus;
            pq.pop();
        }
        ret += pq.size() * minus;
        printf("%lld ", ret);
    }
    return 0;
}
cs

3. Cow Dance Snow
소들이 춤추는 시간들이 정해져있고 춤을 다 춘 소는 순차적으로 들어간다.
모든 소가 춤을 추는데 걸리는 시간 T를 넘지 않도록 소들이 춤출 수 있는 
최소 사이즈 K를 구하는 문제다. 

이분탐색과 위의 문제처럼 pq를 이용한 기법으로 해결할 수 있다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 6;
int n, t;
ll arr[MAXN];
bool ispossible(int idx) {
    ll prv = 0, sum = 0;
    priority_queue<ll> pq;
    for (int i = 0; i < idx; i++) pq.push(-arr[i]);
    while (!pq.empty()) {
        ll curr = -pq.top();
        pq.pop();
        sum += (curr - prv);
        if (sum > t) return false;
        prv = curr;
        if (idx < n) pq.push(-sum - arr[idx++]);
    }
    return sum <= t;
}
int main() {
    scanf("%d%d"&n, &t);
    for (int i = 0; i < n; i++scanf("%lld"&arr[i]);
    
    int l = 1, r = n;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (ispossible(mid)) r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", l);
    return 0;
}
cs

2017년 12월 28일 목요일

Codeforces #455 (Div.2) C - Python Indentation

Codeforces #455 (Div.2) C - Python Indentation http://codeforces.com/contest/909/problem/C

파이썬에서는 들여쓰기로 문법이 적용된다.
for문과 state문 여부가 주어질 때 나타날 수 있는 경우의 수를 구하는 문제이다.

풀어놓고 한번 제출했는데 "메모리초과가 나네? 어차피 틀릴거 놨둬야지" 라고 생각하고 제출안했다...

$dp[idx][level]$ : $idx$에서 들여쓰기가 $level$일 때 경우의 수
$s[idx][level]$ : $idx$에서 $level$부터 N까지 경우의 수의 합
4가지 경우([이전,현재]) 로 나누어 생각해 보았다.

1. [f,f]인 경우
for문 두 번이 연속으로 되어있는 경우는 바로 다음 줄로 이어가야한다.
따라서 $dp[idx][level] = dp[idx-1][level-1]$

2. [s,f]인 경우
state문 다음에 for문이 오므로 state문에 도달한 level 이전까지로 다음 for문이 나타날 수 있다.
$dp[idx][level] = s[idx - 1][level]$

3. [f,s]인 경우
for문다음에 state문이 오면 다음 줄로 이어가야한다.
1과같은 경우가 될수밖에 없다.

4. [s,s]인 경우
2와 같은 경우가 된다.

여기서 2,4인 경우를 구하기 위해 누적 경우의 수를 구해야한다.
누적 경우의 수를 bottom-up방식으로 하면 누적 경우의 수를 구해놓을 수 있다.
#include <cstdio>
#include <iostream>
#include <string>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <iomanip>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define mp(a,b) make_pair(a,b)
#define mt(a,b,c) mp(a,mp(b,c))
#define mf(a,b,c,d) mp(mp(a,b),mp(c,d))
#define mod (ll)(1e9+7)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
int dy[] = { 001-1 };     //동, 서, 남, 북        
int dx[] = { 1-100 };
int N;
char t[5022];
ll dp[5022][5022];
ll s[5022];
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf(" %c"&t[n]);
 
    dp[0][0= 1;
    for (int idx = 1;idx < N;idx++) {
        memset(s, 0sizeof s);
        for (int level = N - 1;level >= 0;level--) {
            s[level] += s[level + 1] % mod + dp[idx - 1][level] % mod;
            s[level] %= mod;
        }
        for (int level = 0;level < N;level++) {
            if (t[idx] == 'f') {
                if (t[idx - 1== 'f') {
                    if (level - 1 >= 0) {
                        dp[idx][level] += dp[idx - 1][level - 1];
                        dp[idx][level] %= mod;
                    }
                }
                else {
                    dp[idx][level] += s[level];
                    dp[idx][level] %= mod;
                }
            }
            else {    // 's'
                if (t[idx - 1== 'f') {
                    if (level - 1 >= 0) {
                        dp[idx][level] += dp[idx - 1][level - 1];
                        dp[idx][level] %= mod;
                    }
                }
                else {
                    dp[idx][level] += s[level];
                    dp[idx][level] %= mod;
                }
            }
        }
    }
    ll ans = 0;
    for (int level = 0;level < N;level++) {
        ans += dp[N - 1][level];
        ans %= mod;
    }
    printf("%lld\n", ans);
    return 0;
}
cs
너무 아까운 문제였다 ㅠㅠ
dp도 거의 top-down방식으로 사용하는데 bottom-up방식도 연습해놔야겠다.

2017년 12월 27일 수요일

Codeforces #432 (Div.2) D - Arpa and a list of numbers

Codeforces #432 (Div.2) D - Arpa and a list of numbers
http://codeforces.com/contest/851/problem/D

수열이 주어진다.
각각의 수열값에 대하여 연산을 시행할 수 있다.
연산은 다음과 같이 두가지가 있다.
1. 해당 숫자를 없앤다. (cost x소모)
2. 숫자를 1 올린다. (cost y소모)
수열의 gcd를 1이 아니도록 만들때 소모되는 최소 cost를 구하는 문제이다.

당연히 대회때는 풀지 못했고 솔루션과 AC코드들을 참고해서 이해하는데도 한참걸렸다.

우선 수열의 값이 최대 $10^6$들어오므로 gcd를 2부터 $10^6$까지 올려보면서
수열들의 gcd를 현재 값으로 맞출때의 최소 cost를 구하면 된다.

최소 cost를 구하는 방법은 다음과 같다.
구간 [index + 1, index + gcd - 1]에 있는 $f$를 기준으로 [index + 1, $f$ - 1]에 포함되는 숫자들은 없애고 
[$f$, index + gcd - 1]에 포함되는 숫자들은 index + gcd로 만드는것이다.

이 $f$를 구하는 방법은 이분탐색으로 구해도 되지만 간단하게 gcd - x/y로 구할 수도 있다.

숫자들을 없애는 연산이나 1씩증가시키는 연산에 대해서는 구간합과 구간개수를 이용하면 된다.


#include <cstdio>
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define MAX 1005555
long long N, x, y;
long long sum[MAX + 1], cnt[MAX + 1];
long long gcnt(int r, int l) {
    if (r < l || l >= MAX) return 0;
    r = min(r, MAX - 1);
    return cnt[r] -  (l <= 0 ? 0 : cnt[l - 1]);
}
long long gsum(int r, int l) {
    if (r < l || l >= MAX) return 0;
    r = min(r, MAX - 1);
    return sum[r] - (l <= 0 ? 0 : sum[l - 1]);
}
int main() {
    scanf("%lld%lld%lld"&N, &x, &y);
    for (int n = 0, in;n < N;n++) {
        scanf("%d"&in);
        sum[in] += in;
        cnt[in]++;
    }
    for (int n = 1;n <= MAX;n++) {
        sum[n] += sum[n - 1];    
        cnt[n] += cnt[n - 1];
    }
    long long ans = N*x;
    for (int g = 2;g < MAX;g++) {
        int f = max(1, g - x / y);
        long long query = 0;
        for (int plus = 0; plus < MAX;plus += g) {
            // [plus + 1, plus + f - 1] delete
            query += gcnt(plus + f - 1, plus + 1* x;
            // [plus + f, plus + g - 1] plus
            query += (gcnt(plus + g - 1, plus + f)*((plus + g) * 1LL) - gsum(plus + g - 1, plus + f))*y;
        }
        ans = min(ans, query);
    }
    printf("%lld\n", ans);
    return 0;
}
cs

2017년 9월 26일 화요일

(DP - 최적의 순서로 맞추기) 13448 SW 역량 테스트

13448 SW 역량 테스트 https://www.acmicpc.net/problem/13448
Codeforces #436 (Div.2) E - Fire http://codeforces.com/contest/864/problem/E

dp는 최적 부분구조를 이뤄야 시행될 수 있다.
만약 주어지는 입력값이 작으면 bitmask dpset 또는 map에 저장시켜 풀 수 있지만
그 값이 크면 대부분 TLE가 발생한다.
그러므로 최적 부분구조를 만드는것이 중요하다.

1. SW 역량 테스트
T분 동안 진행되는 시험에 N개의 문제가 나온다.
$i$번 문제를 $t$분만에 풀면 $M_{i}-t*P_{i}$점을 얻게된다.
$i$번 문제 푸는 시간은 $R_{i}$이다.
얻을 수 있는 점수의 최댓값을 구하는 문제다.

얼마전에 삼성 SW역량 테스트 A형을 봤는데 생각나게끔 하는 문제다.
사람이 생각보다 많이 왔고 A형이라 매우 쉽게 나왔다.

어쨌든 문제로 돌아가면 주어진 문제들의 정보를 입력 그대로 푼다면 위에서 언급한
bitmask dpset정도를 사용해야한다. (하지만 TLE)

따라서 어떻게든 정렬을 해야하는데 수식을 전개하면 아주 놀라운 일이 벌어진다.
https://www.acmicpc.net/board/view/11824
(수식 전개는 별로어렵지 않으니 직접해보는것 추천)

요약 : $R_{i}/P_{i} < R_{j}/P_{j}$ 이면 $i$를 푼 후 $j$를 푸는것이 낫다.
따라서 정렬한 후에 dp로 풀면된다.
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
struct Save { 
    long long M, P, R; 
    double pivot;
};
int N, T;
Save save[51];
long long dp[51][100001];
long long solve(int idx, long long time) {
    if (idx == N) return 0;
    long long &ret = dp[idx][time];
    if (ret != -1return ret;
    ret = 0;
    if (idx + 1 <= N && time + save[idx].R <= T)
        ret = max(ret, solve(idx + 1, time + save[idx].R) + save[idx].M - (time + save[idx].R)*save[idx].P);
    if (idx + 1 <= N)
        ret = max(ret, solve(idx + 1, time));
    return ret;
}
int main() {
    memset(dp, -1sizeof dp);
    scanf("%d%d"&N, &T);
    for (int n = 0;n < N;n++scanf("%lld"&save[n].M);
    for (int n = 0;n < N;n++scanf("%lld"&save[n].P);
    for (int n = 0;n < N;n++scanf("%lld"&save[n].R);
    for (int n = 0;n < N;n++) save[n].pivot = save[n].R / (double)save[n].P;
    sort(save, save + N, [](Save const &a, Save const &b)->bool {
        return a.pivot < b.pivot;
    });
    printf("%lld\n", solve(00));
    return 0;
}
cs

2. Codeforces #436 (Div.2) E - Fire
집에 불이 났다.
집에는 값어치가 매겨진 물품들이 있고 그 물품 $i$를 구출하는데 
걸리는 시간 $t_{i}$, 다 타버리는 시간 $d_{i}$, 물품의 가치 $p_{i}$가 주어진다.
구조할 수 있는 물품의 가치의 합을 최대화하도록 만들고 어떤 물품을 구조했는지 구하는 문제다.

이 문제도 최적 부분구조를 만들어야한다.
바로 $d_{i}$를 최소화 하는것부터 구출하면 최적 부분구조를 이룰 수 있다.
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define mp(a,b) make_pair(a,b)
#define mt(a,b,c) mp(a,mp(b,c))
#define mf(a,b,c,d) mp(mp(a,b),mp(c,d))
typedef long long ll;
struct Save {
    int t, d, p;
    int idx;
};
int N;
Save arr[101];
vector<int> ans;
int dp[101][2002];
int solve(int idx, int time) {
    if (idx >= N) return dp[idx][time] = 0;
    int &ret = dp[idx][time];
    if (ret != -1return ret;
    ret = 0;
    if (time + arr[idx].t < arr[idx].d) {
        ret = max(ret, solve(idx + 1, time + arr[idx].t) + arr[idx].p);
    }
    ret = max(ret, solve(idx + 1, time));
    return ret;
}
void reconstruct(int idx, int time) {
    if (idx >= N) return;
    int here = dp[idx][time];
    if (time + arr[idx].t < arr[idx].d && here == dp[idx + 1][time + arr[idx].t] + arr[idx].p) {
        ans.push_back(idx);
        reconstruct(idx + 1, time + arr[idx].t);
        return;
    }
    reconstruct(idx + 1, time);
}
int main() {
    memset(dp, -1sizeof dp);
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf("%d%d%d"&arr[n].t, &arr[n].d, &arr[n].p), arr[n].idx = n;
    sort(arr, arr + N, [](Save &a, Save &b)->bool {
        return a.d == b.d ? a.t < b.t : a.d < b.d;
    });
    printf("%d\n", solve(00));
    reconstruct(00);
    printf("%d\n", ans.size());
    for (int &next : ans)printf("%d ", arr[next].idx + 1);
    return 0;
}
cs