본문 바로가기

문제 풀이/그외

Code Jam - Qualification Round 2021

Reversort

 

Code Jam - Google’s Coding Competitions

Put your coding skills to the test as you work your way through multiple rounds of algorithmic coding puzzles for the title of Code Jam Champ and 15,000 USD.

codingcompetitions.withgoogle.com

문제에 Reversort이라는 정렬방법이 주어진다.

배열이 주어지면, Reversort를 이용해 정렬하는데 필요한 비용을 구해야 한다.

 

하라는대로 구현하면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
typedef pair<intint> pii;
typedef pair<ll, ll> pll;
 
ll gcd(ll a, ll b) { for (; b; a %= b, swap(a, b)); return a; }
 
int n;
int l[101];
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
 
    int t; cin >> t;
    for (int tc = 1; tc <= t; tc++)
    {
        cin >> n;
        for (int i = 0; i < n; i++cin >> l[i];
 
        int ans = 0;
        for (int i = 0; i < n - 1; i++)
        {
            auto it = min_element(l + i, l + n);
            ans += it - l - i + 1;
            reverse(l + i, it + 1);
        }
 
        cout << "Case #" << tc << ": " << ans << '\n';
    }
}
 

Moons and Umbrellas

 

Code Jam - Google’s Coding Competitions

Put your coding skills to the test as you work your way through multiple rounds of algorithmic coding puzzles for the title of Code Jam Champ and 15,000 USD.

codingcompetitions.withgoogle.com

'C', 'J', '?' 3개의 문자로만 이루어진 문자열 \(s\)가 주어진다.

이 문자열에 "CJ"와 같은 부분문자열 하나당 \(x\), "JC"와 같은 부분문자열 하나당 \(y\)의 비용이 든다.

'?'를 'C'와 'J'중 하나로 치환하여, 필요한 비용의 최소값을 구해야 한다.

 

\(O(|s|)\) DP로 해결할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
typedef pair<intint> pii;
typedef pair<ll, ll> pll;
 
ll gcd(ll a, ll b) { for (; b; a %= b, swap(a, b)); return a; }
 
const int INF = 1e9;
int x, y;
string s;
int dp[2][1001];
int solve(int cj, int idx)
{
    if (s[idx] != '?' && (s[idx] == 'C'!= (cj == 0)) return INF;
    int& ret = dp[cj][idx];
    if (ret != INF) return ret;
    ret = 0;
    if (idx + 1 < s.size())
    {
        int r1 = solve(cj, idx + 1);
        int r2 = solve(1 - cj, idx + 1+ (cj ? y : x);
 
        ret = min(r1, r2);
    }
    return ret;
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
 
    int t; cin >> t;
    for (int tc = 1; tc <= t; tc++)
    {
        cin >> x >> y;
        cin >> s;
 
        for (int i = 0; i < s.size(); i++)
            dp[0][i] = dp[1][i] = INF;
 
        int res = min(solve(00), solve(10));
 
        cout << "Case #" << tc << ": " << res << '\n';
    }
}
 

Reversort Engineering

 

Code Jam - Google’s Coding Competitions

Put your coding skills to the test as you work your way through multiple rounds of algorithmic coding puzzles for the title of Code Jam Champ and 15,000 USD.

codingcompetitions.withgoogle.com

첫번째 문제의 Reversort의 역문제이다.

배열의 길이 \(n\)과 비용 \(c\)가 주어지면, Reversort했을 때 비용이 \(c\)가 드는 \(n\)길이의 배열을 출력해야 한다.

 

먼저 비용이 \(n-1\)보다 작거나, \(\frac{n(n+1)}{2}-1\)보다 크다면 불가능하다.

그렇지 않다면, \(k\)번째로 Reverse연산을 할 때 최소 1, 최대 \(n+1-i\)의 비용이 드므로 적당히 비용을 분배하자.

정렬된 배열에서 연산 역순으로 배열을 직접 뒤집어 보면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
typedef pair<intint> pii;
typedef pair<ll, ll> pll;
 
ll gcd(ll a, ll b) { for (; b; a %= b, swap(a, b)); return a; }
 
int n, c;
int ans[101];
int rv[101];
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
 
    int t; cin >> t;
    for (int tc = 1; tc <= t; tc++)
    {
        cin >> n >> c;
        if (c < n - 1 || c > n * (n + 1/ 2 - 1)
        {
            cout << "Case #" << tc << ": IMPOSSIBLE\n";
            continue;
        }
 
        c -= n - 1;
        for (int i = 1; i <= n; i++)
        {
            ans[i] = i;
            int res = min(c, n - i);
            c -= res;
            rv[i] = res + 1;
        }
 
        for (int i = n - 1; i >= 1; i--)
        {
            reverse(ans + i, ans + i + rv[i]);
        }
 
        cout << "Case #" << tc << ": ";
        for (int i = 1; i <= n; i++cout << ans[i] << ' ';
        cout << '\n';
    }
}
 

Median Sort

 

Code Jam - Google’s Coding Competitions

Put your coding skills to the test as you work your way through multiple rounds of algorithmic coding puzzles for the title of Code Jam Champ and 15,000 USD.

codingcompetitions.withgoogle.com

\(n\)길이의 서로 다른 수로 이루어진 배열 \(x\)가 있다.

배열의 세 원소 \(x_i, x_j, x_k\)중에 중앙값이 무엇인지 알 수 있는 쿼리를 총 \(Q\)번 사용할 수 있을 때,

배열을 정렬한 결과를 알아내야 한다.

 

먼저 \(x_1, x_2, x_3\)에 대해 쿼리를 날린 다음,

중앙값을 제외하고 하나를 최소값, 하나를 최대값으로 설정한 다음, 정렬된 배열 \(a\)로 만들자.

이 배열에 Insertion Sort로 값을 추가할 수 있다.

 

각 값이 배열의 어디에 존재해야 하는지 삼분탐색을 이용해 알 수 있다.

(여기서의 삼분탐색은, 일반적인 의미의 삼분탐색이 아니라 단순히 구간을 3개로 나누는 것을 말한다)

 

범위 \([l, r)\) 안에 값 \(y\)를 추가하려고 하고, 구간의 삼분점을 각각 \(m1, m2\)라고 하자.

 

\(query(a_{m1}, a_{m2}, y\)의 값을 \(res\)라고 한다면,

1. \(res = y\)일 때 : \(y\)는 \([m1, m2)\)에 있어야 한다.

2. \(res = a_{m1}\)일 때 : \(y\)는 \([l, m1)\)에 있어야 한다.

3. \(res = a_{m2}\)일 때 : \(y\)는 \([m2, r)\)에 있어야 한다.

 

총 \(O(n\log n)\)회의 쿼리를 사용하므로 Test Set 2까지 무난히 통과할 수 있는데,

쿼리 개수에 대해 적절한 상수커팅을 더해주면 Test Set 3까지 통과가 가능하다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
typedef pair<intint> pii;
typedef pair<ll, ll> pll;
 
ll gcd(ll a, ll b) { for (; b; a %= b, swap(a, b)); return a; }
 
const int Q = 17000;
 
int n, q;
vector <int> ans;
 
int query_cnt = 0;
 
int query(int a, int b, int c)
{
    if (++query_cnt > Q) exit(0);
 
    cout << a << ' ' << b << ' ' << c << endl;
    int res; cin >> res;
    return res;
}
 
int main()
{
    int t; cin >> t >> n >> q;
    for (int tc = 1; tc <= t; tc++)
    {
        ans.clear();
 
        int res = query(123);
        if (res == 1)
        {
            ans.push_back(2);
            ans.push_back(1);
            ans.push_back(3);
        }
        else if (res == 2)
        {
            ans.push_back(1);
            ans.push_back(2);
            ans.push_back(3);
        }
        else
        {
            ans.push_back(1);
            ans.push_back(3);
            ans.push_back(2);
        }
 
        for (int i = 4; i <= n; i++)
        {
            bool isFirst = false;
            int l = 0, r = ans.size();
            if (l + 1 < r)
            {
                int m = (l + r) / 2;
                int res = query(ans[l], ans[m], i);
                if (res == ans[l]) isFirst = true;
                else if (res == ans[m]) l = m;
                else r = m;
            }
 
            if (isFirst)
            {
                ans.insert(ans.begin(), i);
                continue;
            }
 
            while (l + 2 < r)
            {
                int m1 = (l * 2 + r) / 3;
                int m2 = (l + r * 2/ 3;
 
                int res = query(ans[m1], ans[m2], i);
                if (res == ans[m1]) r = m1;
                else if (res == ans[m2]) l = m2;
                else l = m1, r = m2;
            }
 
            while (l + 1 < r)
            {
                int m = (l + r) / 2;
                res = query(ans[l], ans[m], i);
 
                if (res == i) r = m;
                else l = m;
            }
 
            ans.insert(ans.begin() + l + 1, i);
        }
 
        for (int x : ans) cout << x << ' ';
        cout << endl;
 
        int k; cin >> k;
        if (k != 1return 0;
    }
}
 

Cheating Detection

 

Code Jam - Google’s Coding Competitions

Put your coding skills to the test as you work your way through multiple rounds of algorithmic coding puzzles for the title of Code Jam Champ and 15,000 USD.

codingcompetitions.withgoogle.com

100명의 사람이 10000개의 문제를 푸려고 한다.

각 사람은 \(S_i\)라는 실력을 가지고 있고, 각 문제는 \(Q_j\)라는 난이도를 가지고 있다.

각 값은 \([-3.00, 3.00]\)의 구간 내에 존재한다.

 

\(i\)번째 사람이 \(j\)번째 문제를 풀 때 문제를 맞출 확률은 \(\frac{1}{1+e^{-x}}\)이다.

 

이 중 한 사람이 치팅을 하고 있다.

치팅을 하는 사람은 1/2의 확률로 문제를 무조건 맞춘다.

 

각 사람이 문제를 맞췄는지 여부가 주어질 때, 치팅을 하는 사람이 누구인지,

최소 86%의 확률로 맞춰야 한다.

 

 

각 사람이 맞춘 문제수를 바탕으로 대략적인 \(S_i\)를 복원할 수 있다.

마찬가지로 각 문제가 풀린 횟수를 바탕으로 \(Q_j\)를 복원할 수 있다.

 

두 문제 \(a,b\)에 대해, \(a\)가 \(b\)보다 더 쉬운 문제라고 하자.

\(a\)는 틀리고, \(b\)는 맞춘 횟수를 모든 \((a,b)\)쌍에 대해 계산할 수 있다.

이 값을 \(rev_i\)라고 하자.

치팅을 하는 사람은 \(rev_i\)값이 다른 사람들에 비해 높을것이라 예상할 수 있다.

 

하지만 한가지 문제가 있다.

어떤 사람의 실력이 3.00이나 -3.00에 가까질수록,

다시 말하면 맞은 문제의 수나 틀린 문제의 수 둘 중 한쪽이 많아질수록 \(rev_i\)값은 작아지게 된다.

 

따라서 일반적으로 \(S_i\)인 실력인 사람이 가지는 평균적인 \(rev_i\)값을 \(avg_i\)라고 할 때,

\(\frac{rev_i}{avg_i}\)의 값을 대신 비교함으로써 문제를 해결할 수 있다.

 

\(avg_i\)의 값은 같은 \(S_i\)의 실력인 사람 10명에 대해 직접 시뮬레이션 해보고,

(\(S_i, Q_j\)를 복원해놨으므로 가능하다.) 그 평균값을 사용했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <bits/stdc++.h>
#include <random>
using namespace std;
 
typedef long long ll;
typedef pair<intint> pii;
typedef pair<ll, ll> pll;
 
ll gcd(ll a, ll b) { for (; b; a %= b, swap(a, b)); return a; }
 
int p;
string q[101];
 
int h_cnt[101];
int p_cnt[10001];
double h_p[101];
double p_p[10001];
double h_res[101];
double p_res[10001];
 
double solve(double d)
{
    double l = -3.0, r = 3.0;
    for (int i = 0; i < 100; i++)
    {
        double m = (l + r) / 2;
        double cur = (log(exp(m + 3+ 1- log(exp(m - 3+ 1)) / 6;
        if (d > cur) l = m;
        else r = m;
    }
 
    return l;
}
 
int rev[101];
int avg_rev[101];
double rat_rev[101];
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
 
    uniform_real_distribution<double> unif(0.01.0);
    default_random_engine re;
 
    //freopen("ts2_input.txt", "r", stdin);
 
    int t; cin >> t >> p;
    for (int tc = 1; tc <= t; tc++)
    {
        memset(h_cnt, 0sizeof h_cnt);
        memset(p_cnt, 0sizeof p_cnt);
        memset(rev, 0sizeof rev);
        memset(avg_rev, 0sizeof avg_rev);
 
        for (int i = 0; i < 100; i++cin >> q[i];
 
        for (int i = 0; i < 100; i++for (int j = 0; j < 10000; j++)
        {
            if (q[i][j] == '1')
            {
                h_cnt[i]++;
                p_cnt[j]++;
            }
        }
 
        for (int i = 0; i < 100; i++)
        {
            h_p[i] = (double)h_cnt[i] / 10000;
            h_res[i] = solve(h_p[i]);
        }
 
        for (int i = 0; i < 10000; i++)
        {
            p_p[i] = (double)p_cnt[i] / 100;
            p_res[i] = -solve(p_p[i]);
        }
 
        int tmp[10001];
        for (int i = 0; i < 10000; i++) tmp[i] = i;
        sort(tmp, tmp + 10000, [](int a, int b) {return p_cnt[a] < p_cnt[b]; });
 
        const int LPCNT = 10;
        for (int i = 0; i < 100; i++)
        {
            int cnt = 0;
            for (int j = 0; j < 10000; j++)
            {
                if (q[i][tmp[j]] == '1') cnt++;
                else rev[i] += cnt;
            }
 
            for (int k = 0; k < LPCNT; k++)
            {
                cnt = 0;
                for (int j = 0; j < 10000; j++)
                {
                    double cp = 1.0 / (1.0 + exp(-(h_res[i] - p_res[tmp[j]])));
                    if (unif(re) < cp) cnt++;
                    else avg_rev[i] += cnt;
                }
            }
 
            rat_rev[i] = (double)rev[i] / ((double)avg_rev[i] / LPCNT);
        }
 
        int ans = max_element(rat_rev, rat_rev + 100- rat_rev + 1;
        cout << "Case #" << tc << ": " << ans << '\n';
    }
}
 

'문제 풀이 > 그외' 카테고리의 다른 글

SCPC 2021 Round 1  (2) 2021.07.17
Round A 2021 - Kick Start 2021  (0) 2021.03.21
Google Kick Start - Round F 2020  (0) 2020.09.27
Google Kick Start - Round E 2020  (2) 2020.08.23
SCPC 2020 Round 1  (2) 2020.08.22