ACM-ICPC 2016 Qingdao Preliminary Contest

本文精选了多项算法竞赛题目,包括最小分解数、分数求和、茶水分配、平衡游戏、最佳路径、排序策略及草药采集等,通过详细解析提供高效解题思路,如预处理、二分查找、优先队列、搜索算法等,适用于ACM/ICPC等竞赛准备。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 A. I Count Two Three

题意:找出最小的不小于n的可分解为2^a3^b5^c7^d的形式的数

思路:考虑到n的范围不大,且2/3/5/7的指数也不大,可以直接预处理出答案,然后二分查找

#include<cstdio>
#include<algorithm>
using namespace std;
const long long N = 1e9 + 10;
vector<long long> v;
long long quickpow(long long x, int n) {
    long long res = 1;
    while(n) {
        if(n & 1)
            res = res * x;
        x = x * x;
        n /= 2;
    }
    return res;
}
void solve() {
    long long x, y, z, u;
    for(int i = 0; i <= 30; i += 1) {
        x = quickpow(2, i);
        for(int j = 0; j <= 19; j += 1) {
            y = quickpow(3, j);
            if(x * y > N)
                continue;
            for(int k = 0; k <= 13; k += 1) {
                z = quickpow(5, k);
                if(x * y * z > N)
                    continue;
                for(int p = 0; p <= 11; p += 1) {
                    u = quickpow(7, p);
                    if(x * y * z * u > N)
                        continue;
                    v.push_back(x * y * z * u);
                }
            }
        }
    }
    sort(v.begin(), v.end());
}
int main() {
    solve();
    int T;
    scanf("%d", &T);
    while(T--) {
        int n;
        scanf("%d", &n);
        printf("%lld\n", *(lower_bound(v.begin(), v.end(), (long long)n)));
    }
    return 0;
}

 B. Cure

题意:给你一个整数n,让你输出i∈[1...n]的1/i^2的和

思路:当n较大时,1/i^2的结果已经接近于零,找出这个i,之后的n>i答案都相同,所有也是预处理答案

#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6;
double res[N];
char str[2000];
int main() {
    for(long long i = 1; i <= N; i += 1)
        res[i] = res[i - 1] + 1.0 / (i * i);
    while(~scanf("%s", str)) {
        int l = strlen(str);
        if(l > 7)
            printf("%.5f\n", res[N]);
        else {
            int x = 0;
            for(int i = 0; i < l; i += 1)
                x = x * 10 + str[i] - '0';
            if(x > N)
                x = N;
            printf("%.5f\n", res[x]);
        }
    }
    return 0;
}

 D. Tea

思维

题意:初始有一个茶壶,不知道里面具体有多少茶水,但给你一个茶水体积的范围[l,r],现让你往两个茶杯里倒水,倒到最后两个茶杯里的水体积相差不超过1,茶壶里的水剩余量也不超过1,问最少需要操作的次数

思路:首先分四种情况考虑,

1.r<=1 ,茶壶里的水最多为1,可以不用倒 ans = 0

2.r<=2 ,往任意一个茶杯里倒1的水便结束 ans = 1

3.l==r || r-l==1 , 倒两次l/2结束, ans = 2

4.r-l>1  前两次分别倒l/2 l/2+1 之后轮着倒2,始终保持两茶杯里水1的体积差,因为茶壶最后可以保留一体积的水,所以答案是 2+(r-l-1)/2 = (r - l) /2 + 1

#include<cstdio>
using namespace std;
int main() {
    long long l, r;
    while(~scanf("%lld%lld", &l, &r)) {
        if(r <= 1)
            printf("0\n");
        else if(r <= 2)
            printf("1\n");
        else if(l == r || r - l == 1)
            printf("2\n");
        else {
            if(l == 0)
                l = 1;
            printf("%lld\n", (r - l) / 2 + 1);
        }
    }
    return 0;
}

 

E. Balanced Game

题意:石头剪刀布存在一种平衡局面,即每个人克除他以外一半的人,还被除他以外一半的人克,问给你人数,是否能构成这样一种局面

思路:n为奇数时成立

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n;
        scanf("%d", &n);
        if(n % 2)
            printf("Balanced\n");
        else
            printf("Bad\n");
    }
    return 0;
}

F. The Best Path

图论 欧拉回路/欧拉路径

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int arr[N], d[N], ver, link;
int solve() {
    int type = 0;
    int res = 0;
    for(int i = 1; i <= ver; ++i) {
        if(d[i] & 1)
            type += 1;
    }
    if(type != 0 && type != 2)
        return -1;
    for(int i = 1; i <= ver; ++i) {
        d[i]=(d[i]+1)/2;
        if(d[i] & 1)
            res ^= arr[i];
    }
    if(!type) {
        for(int i = 1; i <= ver; ++i)
            res = max(res, res ^ arr[i]);
    }
    return res;
}
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &ver, & link);
        for(int i = 1; i <= ver; ++i) {
            scanf("%d", &arr[i]);
            d[i] = 0;
        }
        int u, v;
        for(int i = 0; i < link; ++i) {
            scanf("%d%d", &u, &v);
            d[u] += 1;
            d[v] += 1;
        }
        int res = solve();
        if(res == -1)
            printf("Impossible\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

 G. Sort

题意:一个长度为n的序列,每次你可以选择序列中的<=k个数合并,合并一次的代价是所选择的数的和,题目要求将所有数合并为1个,且代价不超过T,问最小的k是多少?

思路:

二分+优先队列

先将序列从小到大排序。若每次能合k个,其实是最优的情况(因为每个数实际上被重复算的次数是最少的),但k值的选择并不能保证每次合并都是k个数合并,所以前期做一个处理,先将前(1+(n-1)%(k-1))个数合并成一个数再放回原序列,那么之后每次合并从序列中就都能选前k小的数合并,直到所有数被合并为一个,总代价最小

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int pre[N], arr[N], n, k;
priority_queue<int, vector<int>, greater<int> >que;
bool solve(int M) {
    while(!que.empty())
        que.pop();
    long long res = 0;
    int x = (n - 1) % (M - 1);
    if(x) {
        x += 1;
        res += pre[x];
        que.push(res);
    }
    for(int i = x + 1; i <= n; ++i)
        que.push(arr[i]);
    while(que.size() > 1) {
        long long x = 0;
        int p = min((int)que.size(), M);
        while(p--) {
            x += que.top();
            que.pop();
        }
        res += x;
        if(res > k)
            return 0;
        que.push(x);
    }
    return res <= k;
}
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &arr[i]);
        sort(arr + 1, arr + 1 + n);
        for(int i = 1; i <= n; ++i)
            pre[i] = pre[i - 1] + arr[i];
        int L = 2;
        int R = n;
        int res = n;
        while(L <= R) {
            int M = (L + R) / 2;
            if(solve(M)) {
                res = min(res, M);
                R = M - 1;
            } else
                L = M + 1;
        }
        printf("%d\n", res);
    }
    return 0;
}

 J. Herbs Gathering

由于 cost 和 socre 范围高达1e9所以不能用dp,改用搜索

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
struct data {
    long long cost, score;
    bool operator <(const data u) const {
        return score * u.cost > u.score * cost;
    }
} p[N];
int n;
long long limit, res;
void solve(int pos, long long w, long long c) {
    if(w > res)
        res = w;
    if(pos >= n || c > limit || (double)w + 1.0 * p[pos].score / p[pos].cost * (limit - c) <= (double)res )
        return ;
    if(c + p[pos].cost <= limit)
        solve(pos + 1, w + p[pos].score, c + p[pos].cost);
    solve(pos + 1, w, c);
}
int main() {
    while(~scanf("%d%lld", &n, &limit)) {
        for(int i = 0; i < n; ++i)
            scanf("%lld%lld", &p[i].cost, &p[i].score);
        sort(p, p + n);
        res = 0;
        solve(0, 0, 0);
        printf("%lld\n", res);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值