2011 Google Code Jam 小记

好久没写这种类型的代码,感觉真是退步了很多。 这是我第一次参加Google Code Jam,以前有过报名可是没有做过。 我发现Google Code Jam的题目使用经典算法的几乎没有,都是模拟或者数学题(起码我目前做过的几题是这样)

首先是Qualification Round 四道水题,前两道模拟,后两道数学推公式。 第三道是位运算的,第四道由于那天没空了就没做,反正分数够了,听人说也是推公式的水题。 题目链接:http://code.google.com/codejam/contest/dashboard?c=975485

// Qualification Round -- A
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>

namespace gcj {
    using namespace std;

    struct rebot {
        int passTime;
        int position;
    };

    queue<bool> all;
    queue<int> qo, qb;

    void solve () {
        int t, n, pos, time, tt;
        char rot;
        scanf("%d", &t);
        rebot o, b;

        for (int  i = 0; i < t; i ++) {
            while(qo.empty() == false)
                qo.pop();
            while(qb.empty() == false)
                qb.pop();

            o.passTime = b.passTime = 0;
            o.position = b.position = 1;
            time = 0;

            scanf("%d", &n);
            while (n --) {
                scanf(" %c %d", &rot, &pos);
                bool isO = (rot == 'O');
                all.push(isO);
                if (isO)
                    qo.push(pos);
                else
                    qb.push(pos);
            }

            while (all.size() > 0) {
                bool isO = all.front();
                all.pop();

                if(isO) {
                    tt = max(0, abs(qo.front() - o.position) - o.passTime) + 1;
                    time += tt;
                    b.passTime += tt;
                    o.position = qo.front();
                    o.passTime = 0;
                    qo.pop();
                } else {
                    tt = max(0, abs(qb.front() - b.position) - b.passTime) + 1;
                    time += tt;
                    o.passTime += tt;
                    b.position = qb.front();
                    b.passTime = 0;
                    qb.pop();
                }
            }

            printf("Case #%d: %d\n", i + 1, time);
        }
    }
}

int main () {

    freopen("d:/A-large.in", "r", stdin);
    freopen("d:/A-large.out", "w", stdout);

    gcj::solve();
    return 0;
}
// Qualification Round -- B
#include <iostream>
#include <cstdio>
#include <map>
#include <string>
#include <set>
#include <cmath>

namespace gcj {
    using namespace std;
    string ls;
    map<char, char> combine[256];
    map<char, char>::iterator comItr;
    set<char> opposed[256];
    //set<char>::iterator oppItr;
    //int charNumber[256];

    void init() {
        ls.clear();
        for (int  i = 0; i < 256; i ++) {
            combine[i].clear();
            opposed[i].clear();
        }
        //memset(charNumber, 0, sizeof(charNumber));
    }

    void solve() {
        int t, c, d, n;
        char comStr[5], oppStr[5];
        char input[105];
        scanf(" %d", &t);
        for (int i = 0; i < t; i ++) {
            init();

            scanf(" %d", &c);
            for(int j = 0; j < c; j ++) {
                scanf(" %s", comStr);
                combine[comStr[0]][comStr[1]] = combine[comStr[1]][comStr[0]] = comStr[2];
            }

            scanf(" %d", &d);
            for(int j = 0; j < d; j ++) {
                scanf(" %s", oppStr);
                opposed[oppStr[0]].insert(oppStr[1]);
                opposed[oppStr[1]].insert(oppStr[0]);
            }

            scanf(" %d", &n);
            scanf(" %s", input);
            for(int j = 0; j < n; j ++) {
                char key = input[j];
                bool run = true;
                //oppItr = opposed[key].begin();

                if (ls.size() > 0) {
                    if ((comItr = combine[key].find(ls.back())) != combine[key].end()) {
                        //charNumber[ls.back()] --;
                        ls.pop_back();
                        ls.push_back(comItr->second);
                        //charNumber[comItr->second] ++;
                        run = false;
                    }

                    if (run) {
                        for (int k = 0; k < ls.size(); k ++) {
                            if (opposed[key].find(ls[k]) != opposed[key].end()) {
                                //ls = ls.substr(0, k);
                                ls.clear();
                                run = false;
                            }
                        }
                    }
                    //while (run && oppItr != opposed[key].end()) {
                    //    if (charNumber[*oppItr] > 0) {
                    //        while(ls.back() != *oppItr) {
                    //            charNumber[ls.back()] --;
                    //            ls.pop_back();
                    //        }
                    //        charNumber[ls.back()] --;
                    //        ls.pop_back();
                    //        run = false;
                    //    }
                    //    oppItr ++;
                    //}
                }

                if (run) {
                    ls.push_back(key);
                    //charNumber[key] ++;
                }
            }

            printf("Case #%d: [", i + 1);
            for (int j = 0; j < ls.size(); j ++) {
                if (j > 0) {
                    putchar(',');
                    putchar(' ');
                }
                putchar(ls[j]);
            }
            puts("]");
        }

    }
}

int main () {

    //freopen("d:/GCJ/B-large.in", "r", stdin);
    //freopen("d:/GCJ/B-large.out", "w", stdout);

    gcj::solve();
    return 0;
}
// Qualification Round -- C
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

namespace gcj {
    using namespace std;

    void solve() {
        int t, n, min_val, sum_val, number, sum_xor;

        scanf("%d", &t);
        for (int i = 0; i < t; i ++) {
            min_val = 1<< 20;
            sum_val = sum_xor = 0;
            scanf(" %d", &n);
            for (int j = 0; j < n; j ++) {
                scanf(" %d", &number);
                sum_xor ^= number;
                sum_val += number;
                min_val = min(min_val, number);
            }

            if (sum_xor)
                printf("Case #%d: NO\n", i + 1);
            else
                printf("Case #%d: %d\n", i + 1, sum_val - min_val);
        }

    }
}

int main () {

    //freopen("d:/GCJ/C-large.in", "r", stdin);
    //freopen("d:/GCJ/C-large.out", "w", stdout);

    gcj::solve();
    return 0;
}

然后是Round 1A 2011 状态不好,能力有限,我只看了第一和第二题 题目链接:http://code.google.com/codejam/contest/dashboard?c=1145485 第一题是告诉你一个今天最大比赛数量、今天胜率、总胜率,问数据是否可能达到. 我的想法是正确的,只有在计算符合今日胜率的最小局数和考虑总胜率为100和为0的情况即可,其他经过我简单的证明都是可行的,但是在获取最小的满足“今天胜率”的今日比赛数量的时候少打了一组括号,并且每注意到大数据范围到10^15,直接导致小数据PASS,大数据挂掉. 后来看了大牛的代码发现他用了最大公约数来解这个值,非常好。 贴小数据代码,大数据只要换到64位整数即可

#include <cstdio>
#include <algorithm>

namespace gcj {
    void print(int t, char* str) {
        printf("Case #%d: %s\n", t, str);
    }

    int gcd(int a, int b) {
        return b ? gcd(b, a % b) : a;
    }

    void solve() {
        int t, n, pd, pg, d, g, l, r;
        scanf("%d", &t);
        for (int i = 0; i < t; i++) {
            scanf("%d %d %d", &n, &pd, &pg);
            d = gcd(pd, 100);
            if ((pd < 100 && pg == 100)
                || (pd > 0 && pg == 0)
                || n < 100 / d)
                print(i + 1, "Broken");
            else
                print(i + 1, "Possible");
        }
    }
}

int main () {

    freopen("d:/GCJ/Round1/A-small-practice.in", "r", stdin);
    freopen("d:/GCJ/Round1/A-small-practice.out", "w", stdout);

    gcj::solve();
    return 0;
}

第二题是猜字游戏 看了大牛的代码,用了DFS,两次精妙的索引排序解决了。太强大了,我还有差距呵。

然后是Round 1B 2011 题目链接:http://code.google.com/codejam/contest/dashboard?c=1150485 这个发挥得比较好,拿到了晋级名额。 我也只写了两道题,但是方法都对,而且都是1A,最后一道题我发现只剩下25分钟了,而且很晚了,反正写不完就懒得写了 第一题是模拟,按题目说的做就好了,这题在大数据的时候freopen的路径写错了,结果我一直在想为什么会TLE,还好在提交限时的两分钟前看到了,马上改掉就ok了。

#include <cstdio>
#include <cstring>

namespace gcj {
    using namespace std;

    const int maxn = 105;
    char mat[maxn][maxn];
    double WP[maxn];
    double WP_par[maxn][2];
    double OWP[maxn];
    double OOWP[maxn];

    void solve() {
        int t, n, c = 0;
        scanf("%d", &t);
        while (t --) {
            c ++;
            scanf("%d", &n);
            for (int i = 0; i < n; i ++)
                scanf("%s", mat[i]);

            // WP
            for (int i = 0; i < n; i ++) {
                double sum = 0.0, win = 0.0;
                WP_par[i][0] = WP_par[i][1] = 0.0;
                for (int j = 0; j < n; j ++) {
                    if (mat[i][j] == '.')
                        continue;
                    sum += 1.0;
                    if (mat[i][j] == '1')
                        win += 1.0;
                }
                WP_par[i][0] = win;
                WP_par[i][1] = sum;
                WP[i] = win / sum;
            }

            // OWP
            for (int i = 0; i < n; i ++) {
                double sum = 0.0, win = 0.0;
                for (int j = 0; j < n; j ++) {
                    if (mat[i][j] == '.')
                        continue;
                    sum += 1.0;
                    if (mat[i][j] == '1')
                        win += (WP_par[j][0]) / (WP_par[j][1] - 1.0);
                    else
                        win += (WP_par[j][0] - 1.0) / (WP_par[j][1] - 1.0);
                }
                OWP[i] = win / sum;
            }

            // OOWP
            for (int i = 0; i < n; i ++) {
                double sum = 0.0, win = 0.0;
                for (int j = 0; j < n; j ++) {
                    if (mat[i][j] == '.')
                        continue;
                    sum += 1.0;
                    win += OWP[j];
                }
                OOWP[i] = win / sum;
            }

            // Output
            printf("Case #%d:\n", c);
            for (int i = 0; i < n; i ++) {
                printf("%.12lg\n", 0.25 * WP[i] + 0.50 * OWP[i] + 0.25 * OOWP[i]);
            }
        }
    }

}

int main() {
    freopen("A-large.in", "r", stdin);
    freopen("A-large.out", "w", stdout);
    gcj::solve();
    return 0;
}

第二题是一个数学题,又是数学题,问最少多久能让任意两个热狗店距离不小于d,计算的时候只要考虑往一个方向走,然后最后答案除以2即可,因为运动是对称的。中途需要考虑的是两个相邻组的距离问题,然后对分散时间进行合并取大值,并考虑交叉的情况加上分开交叉情况的时间即可。又是64位整形,不过这次我注意到了。不过那个maxn貌似我忘记改成大数据的数字了,为什么过了,神奇的。

#include <cstdio>
#include <cstring>
#include <algorithm>

namespace gcj {
    using namespace std;

    const int maxn = 206;
    typedef long long lld;

    struct node{
        lld p;
        lld v;
    };
    bool cmp(const node& l, const node& r) {
        return l.p < r.p;
    }

    node venders[maxn];

    void solve() {
        int t, n, _case = 0;
        scanf("%d", &t);
        while (t --) {
            _case ++;
            int d, c;
            scanf("%d %d", &c, &d);
            for (int i = 0; i < c; i ++)
                scanf("%lld %lld", &venders[i].p, &venders[i].v);

            sort(venders, venders + c, cmp);
            lld tmp, tl, last;
            lld sum = (venders[0].v - 1) * d;
            for (int i = 1; i < c; i ++) {
                tmp = (venders[i].v - 1) * d;
                tl = venders[i].p - venders[i].v * d;
                if (tmp > sum) {
                    last = venders[i - 1].p - (tmp - sum);
                    if(tl >= last)
                        sum = tmp;
                    else
                        sum = tmp + last - tl;
                } else {
                    last = venders[i - 1].p;
                    if (tl >= last) {
                        lld _m = min(tl - last, sum - tmp);
                        venders[i].p -= _m;
                    } else {
                        sum += last - tl;
                    }
                }
            }

            printf("Case #%d: %.1lf\n", _case, sum / 2.0);
        }
    }

}

int main() {
    freopen("D:/GCJ/Round2/B-large.in", "r", stdin);
    freopen("D:/GCJ/Round2/B-large.out", "w", stdout);

    gcj::solve();
    return 0;
}

Round2期间正碰上期末考试,但是我还是来做了一下,很失望的没拿到衣服。感觉我的编码能力还差得远呐,因为AC两题应该就能拿到衣服了,可是遗憾在第二题。 A很简单,大意是一个人在一条路上走,可以跑t秒,告诉走路和跑步的速度,再有的是路上有n个不重叠的walkway,在walkway上的速度等于walkway的速度+当前速度,问最少几秒钟到终点。思路非常简单,就是要在walkway上加速尽量多的时间。有两种情况,一种是在没有walkway的路上跑步跑完了t秒,这种情况时间很好算。另一种是walkway之外的路上t秒没用完,这时后要尽量在速度快的walkway上加速,所以对walkway的速度排序就可以了。需要注意的情况就是从头跑到尾的情况。 A题代码:

#include <cstdio>
#include <algorithm>
#include <cstring>

namespace gcj{
    using namespace std;

    const int MAXN = 1005;
    struct walkway{
        int b, e, w;
        friend bool operator< (const walkway& l, const walkway& r) {
            return l.w < r.w;
        }
    };

    walkway cors[MAXN];

    void solve(int T) {
        int x, s, r, t, n;
        int wwlen = 0, rlen;
        double res = 0.0, rt;

        scanf("%d %d %d %d %d", &x, &s, &r, &t, &n);
        for (int  i = 0; i < n; i ++)
            scanf("%d %d %d", &cors[i].b, &cors[i].e, &cors[i].w), wwlen += cors[i].e - cors[i].b;
        rlen = x - wwlen;
        rt = t;
        sort(cors, cors + n);

        if(rlen >= t * r)
            res = 1.0 * (rlen - t * r) / s + t, rt = 0.0;
        else
            res = 1.0 * rlen / r, rt -= res;

        for (int  i = 0; i < n; i ++) {
            if (rt <= 0.0) {
                res += 1.0 * (cors[i].e - cors[i].b) / (s + cors[i].w);
                continue;
            }
            if (1.0 * (cors[i].e - cors[i].b) / (r + cors[i].w) <= rt) {
                res += 1.0 * (cors[i].e - cors[i].b) / (r + cors[i].w);
                rt -= 1.0 * (cors[i].e - cors[i].b) / (r + cors[i].w);
            } else {
                res += 1.0 * (cors[i].e - cors[i].b - (r + cors[i].w) * rt) / (s + cors[i].w) + rt;
                rt = 0.0;
            }
        }

        printf("Case #%d: %.9lf\n", T + 1, res);
    }

    void solve() {
        int t;
        scanf("%d", &t);
        for (int i = 0; i < t; i ++)
            solve(i);
    }
}

int main() {
    freopen("E:/OWenT/GCJ/R2/A-large.in", "r", stdin);
    freopen("E:/OWenT/GCJ/R2/A-large.out", "w", stdout);

    gcj::solve();
    return 0;
}

B题是我的痛啊,如果是难题,我不会,那我无话可说,关键是简单的计算几何+简单的DP,方法对,思路对,写代码的时候犯了N个白痴错误,各种变量名写错,各种复制代码忘了改。以后要注意写代码宁愿慢一点结构严谨,也不要贪图方便Copy代码。 比赛的时候这题我首先理解题目花了40多分钟,花了10分钟推导,50多分钟编码竟然都没写出来,Sample都没过,赛后我完全重写了一下,写出来到过小数据和大数据花了大概40分钟不到。真是不甘心。

B题大意是给一个矩形钢板,要求先裁出一块正方形,边长大于等于3,再去掉四个角,要求最终的东西重心在形状上的中心。问是否存在如果存在给出最大的第一次裁出的正方形边长。题目难理解的地方在于开始输入的有r c d,而给的每个grid的重量是d+wij,这个地方我一直没理解然后用sample测理解。方法就是DP,对R和C进行DP,对K枚举就好。需要注意的地方是k可以为偶数。 B题代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

namespace gcj {
    using namespace std;

    const int maxn = 505;
    typedef long long lld;

    char inputStr[maxn];
    struct grid{
        lld r, c, w;
        grid(): r(0), c(0), w(0){}
        grid(lld a, lld b, lld c): r(a), c(b), w(c){}

        friend grid operator+(const grid& l, const grid& r) {
            return grid(l.r + r.r, l.c + r.c, l.w + r.w);
        }

        friend grid operator-(const grid& l, const grid& r) {
            return grid(l.r - r.r, l.c - r.c, l.w - r.w);
        }
    };

    grid grids[maxn][maxn];
    grid sum[maxn][maxn];

    void solve() {
        int t, r, c, d, res;
        scanf("%d", &t);

        for (int tc = 0; tc < t; tc ++) {
            scanf("%d %d %d", &r, &c, &d);
            res = 0;

            for (int i = 1; i <= r; i ++) {
                scanf(" %s", inputStr);
                for (int j = 1; j <= c; j ++) {
                    grids[i][j].w = d + inputStr[j - 1] - '0';
                    grids[i][j].r = i * grids[i][j].w;
                    grids[i][j].c = j * grids[i][j].w;

                    sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + grids[i][j];
                }
            }

            for (int i = 1; i <= r; i ++) {
                for (int j = 1; j <= c; j ++) {
                    for (int k = max(3, res + 1); k + i - 1 <=r && k + j - 1 <= c; k ++) {
                        int ti = k + i - 1, tj = k + j - 1;
                        //if ((i + ti) % 2 || (j + tj) % 2)
                        //    continue;
                        grid gd = sum[i - 1][j - 1] + sum[ti][tj] - sum[i - 1][tj] - sum[ti][j - 1]
                            - grids[i][j] - grids[i][tj] - grids[ti][j] - grids[ti][tj];
                            if (((i + ti) % 2 + 1) * gd.r % gd.w != 0 || ((j + tj) % 2 + 1) * gd.c % gd.w != 0)
                                continue;
                            if (2 * gd.r / gd.w != i + ti || 2 * gd.c / gd.w != j + tj )
                                continue;
                            res = max(res, k);
                    }
                }
            }

            if(res)
                printf("Case #%d: %d\n", tc + 1, res);
            else
                printf("Case #%d: IMPOSSIBLE\n", tc + 1);
        }
    }
}

int main() {
    freopen("D:/GCJ/R2/B-large-practice.in", "r", stdin);
    freopen("D:/GCJ/R2/B-large-practice.out", "w", stdout);

    gcj::solve();
}

唉,真心还需要学习提高水平啊。

Last updated