机房水题欢乐赛 2016-02-01

49 篇文章 1 订阅
24 篇文章 1 订阅

——暨GDKOI校队选拔赛

T1: 积木分发

单个测试点时间限制: 1s 内存限制: 128MB

描述

歌手Pancakes到幼儿园跟小朋友们玩,他到达时小朋友们正在争抢积木,小朋友都想要更多的积木砌一个自己喜欢的图形,砌完后就可以和Pancakes合照。同时,Pancakes手上还有一些积木,他可以把手上的积木全部给一个小朋友,然后等该小朋友砌完后就可以收回所发的积木和该小朋友原有的积木。但她不知道能否让所有的小朋友都和她合照,聪明的你可以帮助她吗?

输入

输入包含多组数据(每个测试点)。
每个数据的第一行是两个整数n和s,1≤n≤10000,1≤s≤10000,表示一共有n位小朋友,Pancakes手上有s块积木。以下有n行,每行两个正整数,a和b,1≤a,b≤10^9,表示第i个小朋友手上有a块积木,还需要b块积木才能砌完。
输入n=0是表示结束。

输出

如果可以让所有的小朋友都和Pancakes合照,就输出”YES”;否则,输出”NO”。

样例输入

2 2
1 4
2 1
2 2
1 4
1 1
0 0

样例输出

YES
NO

题解(GDKOI 2007 Day 2 T2)

显然,我们贪心地选取需求最少的小朋友即可,所以按照b升序排序,除此之外a的顺序对答案没有影响。如果按顺序中途积木不够就判NO。

程序

#include <cstdio>
#include <algorithm>
using namespace std;
pair<int, int> kid[10001];
int main() {
    int n, s, i, a, b;
    while (scanf("%d%d", &n, &s), n != 0) {
        for (i = 0; i < n; i++) {
            scanf("%d%d", &a, &b);
            kid[i] = make_pair(b, a);
        }
        sort(kid, kid + n);
        for (i = 0; i < n; i++) {
            if (kid[i].first <= s) s += kid[i].second;
            else { puts("NO"); goto end; }
        }
        puts("YES");
end:
        continue;
    }

    return 0;
}

T2: 汉诺塔

题目描述

古老的汉诺塔问题是:用最少的步数将N个半径互不相等的圆盘从1号柱利用2号柱全部移动到3号柱,在移动过程中小盘永远在大盘上边。 现在再加上一个条件:不允许从1号柱直接把盘移动到3号柱, 也不允许从3号柱直接移动到1号柱。把盘按半径从小到大1——N进行编号。每种状态用N个整数表示, 第i个整数表示第i号盘所在的柱的编号。则N=2时的移动方案为(1,1)》(2,1)》(3,1)》(3,2)》(2,2)》(1,2)》(1,3)》(2,3) 》(3,3)初始状态为0步,变成求在某步数时的状态。

输入

输入文件的第一行为整数T(1<=T<=100),表示输入数据的组数。接下来的T行,每行有两个整数N,M(1<=N<=19, 0<=M<=移动N个圆盘需要的次数)

输出

输入文件一共T行对于每组输入数据,输出N个整数表示移动N个盘在M步时的状态,每两个数之间用一个空格隔开,行首和行末不要有多余的空格。

样例输入

3
2 0
2 1
2 2

样例输出

1 1
2 1
3 1

题解

普通的汉诺塔问题移动n个圆盘的次数是

fn=fn1+1+fn1

对应的是:

func hanoi(n,a,b,c) {
    hanoi(n-1,a,c,b)
    move(n,a,c)
    hanoi(n-1,b,a,c)
}

不允许跨柱移动的话就是

func hanoi(n,a,b,c) {
    hanoi(n-1,a,b,c) // 把上面n-1个圆盘从a挪到c
    move(n,a,b) // 把第n个圆盘从a挪到b
    hanoi(n-1,c,b,a) // 把上面n-1个圆盘从c挪到a
    move(n,b,c) // 把第n个圆盘从b挪到c
    hanoi(n-1,a,b,c) // 把上面n-1个圆盘从a挪到c
}

对应的移动圆盘次数就是

fn=fn1+1+fn1+1+fn1

然后按照 fn 的大小和m的关系走就行啦。

#include <cstdio>
typedef long long ll;
ll f[20], state[20];

void hanoi(int x, int y, int z, ll n, ll m) {
    if (!n) return;
    if (m < f[n - 1] + 1) {
        state[n] = x;
        hanoi(x, y, z, n - 1, m);
    } else if (m < f[n - 1] + 1 + f[n - 1] + 1) {
        state[n] = y;
        hanoi(z, y, x, n - 1, m - (f[n - 1] + 1));
    } else {
        state[n] = z;
        hanoi(x, y, z, n - 1, m - (f[n - 1] + 1 + f[n - 1] + 1));
    }
}

int main() {
    int t, i, n, m;
    scanf("%d", &t);
    for (i = 1; i <= 19; ++i)
        f[i] = f[i - 1] + 1 + f[i - 1] + 1 + f[i - 1];

    while (t--) {
        scanf("%d%d", &n, &m);
        hanoi(1, 2, 3, n, m);
        for (i = 1; i < n; i++)
            printf("%lld ", state[i]);
        printf("%lld\n", state[n]);
    }

    return 0;
}

T3: 多项式运算

描述

大家会不会觉得多项式运算是一个很复杂的问题呢?
现在给出多项式的手写形式(如x+1,3x^3-2x^2+2),要求进行各种运算。
运算一共有4种,分别为evaluate(代入),add(加),subtract(减),multiply(乘)。

输入

每个输入数据都有多个运算,行末行首均没有多余空格。所有的字母都是小写,每行都用回车分开。表达式可能出现的字符有0~9,+,-,^,x 任何+,-运算符都不会连续出现两个。下面是几种基本运算的输入格式。
代入,求出当x等于指定值的时候对应的多项式的值:
加法,求出所有的多项式的和(输入-1表示运算结束):
减法,求出第一个多项式减去后面所有多项式的值(输入-1表示减法结束):
乘法,求出所有的多项式的积(输入-1表示乘法结束):
若干行多项式
-1
结束,表示程序的结束
last

输出

输出每种运算以后的结果。首先输出运算的序号,然后紧跟一个冒号,接着是一个空格,最后是运算结果。每种运算的结果占用一行。多项式按降序排列,并且要进行同类项合并。

输入(ploy.in)

add
x+1
2x^2+1
3x+4
-1
evaluate
3
7x^3+2x^2-10
last

输出(ploy.out)

1:2x^2+4x+6
2:197

数据规模

多顶式项数<=500 指数<=10^9

题解

这种东西吧。。纯码农题把代码写好看点大抵不会出事的。

程序

#include <fstream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <cctype>
using namespace std;
typedef long long ll;

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    for (; b; a *= a, b /= 2)
        if (b & 1) ans *= a;
    return ans;
}

struct Expression {
    // power, ratio
    map<ll, ll> exp;
    typedef map<ll, ll>::iterator mii;
    typedef map<ll, ll>::reverse_iterator rmii;

    explicit Expression() {}

    explicit Expression(ll i) { exp[0] = i; }

    explicit Expression(const string &s) {
        static vector<int> q;
        int begin, end, i, j, k;
        ll now, ratio, power, ratio_f, power_f;
        bool found_x;

        q.clear();

        q.push_back(0);
        for (i = 0; i < s.length(); ++i)
            if (s[i] == '+' || (s[i] == '-' && (i == 0 || i > 0 && s[i - 1] != '^'))) q.push_back(i);
        q.push_back(s.length());

        for (i = 0; i < q.size() - 1; ++i) {
            begin = q[i]; end = q[i + 1];
            ratio_f = 1; power_f = 1; now = 0; power = 0; ratio = 0;
            found_x = 0;
            if (s[begin] == '+') ++begin;
            else if (s[begin] == '-') ++begin, ratio_f = -1;
            for (k = begin; k < end; ++k)
                if (isdigit(s[k])) now = now * 10 + s[k] - '0';
                else if (s[k] == '-' && found_x) power_f = -1;
                else if (s[k] == 'x') {
                    if (k == begin) now = 1;
                    ratio = now, now = 0, found_x = 1;
                }
            if (!found_x) ratio = now;
            else { if (s[end - 1] == 'x') now = 1; power = now * power_f; }
            exp[power] += ratio * ratio_f;
        }
    }

    void operator += (const Expression &b) {
        apply(b, 1);
    }

    void operator -= (const Expression &b) {
        apply(b, -1);
    }

    void apply(Expression b, ll ratio) {
        for (mii it = b.exp.begin(); it != b.exp.end(); ++it)
            apply(it->first, it->second * ratio);
    }

    void apply(ll f, ll s) {
        if (s != 0) {
            mii x = exp.find(f);
            if (x != exp.end()) {
                x->second += s;
                if (x->second == 0) exp.erase(x);
            } else exp[f] = s;
        }
    }

    void operator *= (Expression b) {
        Expression tmp;
        for (mii it = exp.begin(); it != exp.end(); ++it)
            for (mii bit = b.exp.begin(); bit != b.exp.end(); ++bit)
                tmp.apply(it->first + bit->first, it->second * bit->second);
        exp = tmp.exp;
    }

    ll eval(ll x) {
        int ans = 0;
        for (mii it = exp.begin(); it != exp.end(); ++it)
            ans += it->second * quick_pow(x, it->first);
        return ans;
    }

    friend ostream &operator <<(ostream &a, Expression &b) {
        bool output = false;
        for (rmii it = b.exp.rbegin(); it != b.exp.rend(); ++it) {
            if (it->second == 0) continue;
            output = true;
            if (it->second > 0 && it != b.exp.rbegin()) a << '+';
            if (it->first != 0 && it->second != 1 || it->first == 0)
                if (it->second == -1 && it->first != 0) a << '-';
                else a << it->second;
            if (it->first != 0) a << 'x';
            if (it->first > 1 || it->first < 0) a << '^' << it->first;
        }
        if (!output) a << '0';
        return a;
    }
};

int main() {
    ifstream fin("poly.in");
    ofstream fout("poly.out");

    string op;
    ll kase = 0, x, c;
    while (fin>>op) {
        if (op == "last") break;
        fout<<++kase<<": ";
        if (op == "evaluate") {
            fin>>x>>op;
            fout<<Expression(op).eval(x)<<endl;
            continue;
        }
        Expression e, firstExpression; e.exp.clear(); bool hasRead = false;
        if (op == "add") c = 1;
        else if (op == "subtract") c = 2;
        else if (op == "multiply") { c = 3; e = Expression(1); }
        while (fin>>op, op != "-1") {
            if (!hasRead) firstExpression = Expression(op), hasRead = true;
            if (c == 1) e += Expression(op);
            else if (c == 2) e -= Expression(op);
            else if (c == 3) e *= Expression(op);
        }
        if (c == 2) { // 因为是第一个表达式减后面的
            e += firstExpression;
            e += firstExpression;
        }
        fout<<e<<endl;
    }

    return 0;
}

T4: 国王的遗产

哈丁国的国王一生善于管理,勤于政务,在国家里聚积了大量的财富。但他众多的孩子都不争气,相互间时常勾心斗角,却没有一个真正能接受国王传位的人。为了避免将来某儿子一人独揽大权,又出于不能让权力过度分散的考虑,临终前,国王作了一个决定:

国王他将一生的财富打造出了一条很大的金块链,这条金块链 的形状比较特别,它由n块大块的黄金组成,国王准备了n-1条链条,将某些相邻的两块大黄金用链条连接起来,最后构成一条连通的金块链。下图是国王构建的一条金块链:
国王对每块黄金编上号(从1到n),然后立下了遗嘱:

  • 儿子们按照年龄大小顺序,在现存的金块链中获得遗产。
  • 对于某个儿子,他可以在现存金块链中剪掉某条链条,获得不超过现有金块总数一半的那一部分。
  • 某个儿子取得他那部分金块后,剩下的部分由他后面的弟弟们继续操作。
  • 最后一个儿子获得剩下的那些金块,国王将保证每个儿子都能获得遗产。

儿子们都是贪婪的,他们都会选取使自己得到最多的金块的那条链条来剪,当然,有时选取的方案不是唯一的,但是儿子们都会选择使自己获得的“金块组编号”最小的那一条链来剪。“金块组编号”大小定义为:对于长度相同为L的两个有序数组A和B,A

输入

输入数据第一行为一个整数n(1≤n≤30000)和一个整数k(1≤k≤100),分别表示金块的总数与国王儿子的数量。接下来n-1行,每行两个整数x和y,表示编号为x的金块与编号为y的金块用链条连接起来。

输出

输出数据只有一行,包含有k个整数,分别表示每个儿子获得金块的数量(由大儿子到最小的儿子)。

样例输入

6 3
1 2
2 3
3 4
2 5
3 6

样例输出

3 1 2

样例说明

这里,大儿子取得金块组{1,2,5},二儿子取得金块组{4},三儿子取得金块组{3,6}。

题解

程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值