训练题(2023-2-2)

文章介绍了几个编程竞赛中的问题,包括使用线性动态规划解决的Nakahashi吃寿司问题,寻找优秀拆分的位运算方法,以及找到不超过给定数的最大2的幂次方的数值问题。此外,还讨论了如何判断具有单纯质因数的合数和在有限时间内最大化价值的策略。这些问题展示了算法在解决数学和计算机科学问题中的重要性。
摘要由CSDN通过智能技术生成

AtCoder Regular Contest 096 D - Static Sushi(线性dp)

Problem Statement

“Teishi-zushi”, a Japanese restaurant, is a plain restaurant with only one round counter. The outer circumference of the counter is C meters. Customers cannot go inside the counter.

Nakahashi entered Teishi-zushi, and he was guided to the counter. Now, there are N pieces of sushi (vinegared rice with seafood and so on) on the counter. The distance measured clockwise from the point where Nakahashi is standing to the point where the i-th sushi is placed, is xi meters. Also, the i-th sushi has a nutritive value of vi kilocalories.

Nakahashi can freely walk around the circumference of the counter. When he reach a point where a sushi is placed, he can eat that sushi and take in its nutrition (naturally, the sushi disappears). However, while walking, he consumes 1 kilocalories per meter.
Whenever he is satisfied, he can leave the restaurant from any place (he does not have to return to the initial place). On balance, at most how much nutrition can he take in before he leaves? That is, what is the maximum possible value of the total nutrition taken in minus the total energy consumed? Assume that there are no other customers, and no new sushi will be added to the counter. Also, since Nakahashi has plenty of nutrition in his body, assume that no matter how much he walks and consumes energy, he never dies from hunger.

Constraints

1≤N≤105
2≤C≤1014
1≤x1<x2<…<xN<C
1≤vi≤109
All values in input are integers.
Subscores
300 points will be awarded for passing the test set satisfying N≤100.
Input
Input is given from Standard Input in the following format:
N C
x1 v1
x2 v2
:
xN vN
Output
If Nakahashi can take in at most c kilocalories on balance before he leaves the restaurant, print c.

样例

Sample Input 1
3 20
2 80
9 120
16 1
Sample Output 1
191

There are three sushi on the counter with a circumference of 20 meters. If he walks two meters clockwise from the initial place, he can eat a sushi of 80 kilocalories. If he walks seven more meters clockwise, he can eat a sushi of 120 kilocalories. If he leaves now, the total nutrition taken in is 200 kilocalories, and the total energy consumed is 9 kilocalories, thus he can take in 191 kilocalories on balance, which is the largest possible value.

题意

n行,每行x[i]和v[i]代表寿司所在的位置和吃完后获得的能量
餐厅里有个圆形柜台周长为C,Nakahashi从1开始,每走1m消耗1k卡能量,Nakahashi可以在任何时候离开餐馆,求Nakahashi所能带走的最大能量

题解

考虑到n<=105,shun[i]顺着走到i,ni[i]逆着走到i
有顺又有逆二重循环i,j判断顺着到i,逆着到j最大
或者只有顺,或者只有逆,3种情况取最大只能拿300分
如何优化掉复杂度呢?
我们可以这样考虑,shun[i]顺着走到i的最大,ni[i]逆着走到i的最大
有顺有逆的话,考虑先顺着走到i,再逆着走到i+1
然后考虑逆着走到i,顺着走到i-1
或者只有顺,或者只有逆

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct peo{
    int x, v;
}p[100000];
ll shun[100000],ni[100000], val[100000], reval[100000];
int main() {
    int n, c;
    cin >> n >> c;
    for (int i = 0; i < n; i++) {
        cin >> p[i].x >> p[i].v;
    }
    for (int i = 0; i < n; i++) {
        val[i] = val[i - 1] + p[i].v;//顺着到i的价值总和
        shun[i] = max(shun[i - 1], val[i] - p[i].x);//顺着到i取最大
    }
    for (int i = n - 1; i >= 0; i--) {
        reval[i] = reval[i + 1] + p[i].v;//逆着到i的价值总和
        ni[i] = max(ni[i + 1], reval[i] - c + p[i].x);//逆着到i取最大
    }
    ll ma = 0;
    for (int i = n - 1; i >= 0; i--) {
        ma = max(ma, reval[i] - 2 * (c - p[i].x) + shun[i - 1]);//先逆着到i,再顺着到i-1
        ma = (ma, ni[i]);//只有逆
    }
    for (int i = 0; i < n; i++) {
        ma = max(ma, val[i] - 2 * p[i].x + ni[i + 1]);//先顺着到i,再逆着到i+1
        ma = max(ma, shun[i]);//只有顺
    }
    cout << ma;
}

优秀的拆分

题目描述

一般来说,一个正整数可以拆分成若干个正整数的和。例如,1=1,10=1+2+3+4等。
对于正整数n的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下,n被分解为了若干个不同的2的正整数次幂。注意,一个数x能被表示成2的正整数次幂,当且仅当x能通过正整数个2相乘在一起得到。
例如,10=8+2=23+ 21是一个优秀的拆分。但是,7=4+2+1=22 +2 1+20就不是一个优秀的拆分,因为1不是2的正整数次幂。
现在,给定正整数n,你需要判断这个数的所有拆分中,是否存在优秀的拆分。若存在,请你给出具体的拆分方案。

输入

输入只有一行,一个正整数n,代表需要判断的数。

输出

如果这个数的所有拆分中,存在优秀的拆分。那么,你需要从大到小输出这个拆分中的每一个数,相邻两个数之间用一个空格隔开。可以证明,在规定了拆分数字的顺序后,该拆分方案是唯一的。
若不存在优秀的拆分,输出“-1”(不包含双引号)。

样例

样例输入
【样例1】
6
【样例2】
7
样例输出
【样例1】
4 2
【样例2】
-1

法一(模拟)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ll n;
    cin >> n;
    if (n % 2) {
        cout << "-1";
        return 0;
    } else {
        while (n > 0) {
            ll p = 1;
            while (n >= p * 2) {
                p *= 2;
            }
            cout << p << " ";
            n -= p;
        }
    }
}

法二(位运算)

#include<bits/stdc++.h>
using namespace std;
int n;
int main() {
    cin >> n;
    if (n & 1)
        cout << -1;
    else {
        for (int i = 31; i >= 1; i--) {//n值范围[1,10^7]所以i枚举范围[31,1],即7位二进制数有28位,为防止超限,取31
            int t = (1 << i);// 或者 t=pow(2, i);
            if (t & n)
                cout << t << " ";
        }
    }
    return 0;
}

最接近

题目描述

小明同学最近正在研究一些幂次的特性,比如 32 = 9 和 43= 64。
小花是小明的好朋友,她总是和小明一起学习,由于小花刚刚学习幂次,很多计算还不太熟练。她很好奇,对于任意给出的正整数 N,不超过 N 的最大的 2 的幂次方的数是多少?
例如:如果 N 等于 10,那么不超过 10 的最大的 2 的幂次方的整数就是 8,如果 N 是 100,那么不超过 100 的最大的 2 的幂次方的整数就是 64。小花希望你能帮助她解决这个问题。如果你能解决这个问题,就能和小花成为好朋友,小花希望朋友越多越好。
【数据范围】
对于 100% 的数据,保证 1 ≤ N ≤ 1018
在这里插入图片描述

输入

输入只有一行一个正整数 N。

输出

输出只有一行一个正整数,表示不超过 N 的最大的 2 的幂次方的正整数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ull a[1000000], c = -1;
int main() {
    for (int i = 0; i < 64; i++) {
        a[c++] =(ull)pow(2, i);
    }
    ll n;
    cin >> n;
    for (int i = 0; i < 64; i++) {
        if (n <a[i]) {
            cout << a[i-1];
            break;
        }
    }
}

单纯质因数

题目描述

读五年级的楠楠刚学完了质数、合数、因数、质因数等概念。
他还知道了每个合数都可以写成几个质数相乘的形式,其中每个质数都是这个合数的因数,叫做这个合数的质因数.把一个合数用质因数相乘的形式表示出来,叫做分解质因数.
聪明爱动脑筋的楠楠突然对具有互不相同的质因数的合数产生了兴趣。例如:30=2 * 3 * 5,它有互不相同的质因数;70=2 * 5 * 7,它也有互不相同的质因数。若一个合数中所有的质因数互不相同,则把它称之为具有单纯质因数的合数。他想知道还有哪些数是单纯质因数的合数。
你现在要帮楠楠解决的问题是:已知N,依次输出N以内所有具有单纯质因数的合数。

输入

输入数据只一个整数(10<=N<=100000)。

输出

依次输出N以内所有具有单纯质因数的合数。

样例

样例输入
12
样例输出
6 10

#include<bits/stdc++.h>
using namespace std;
int n;
bool judge(int x) {
    for (int i = 2; i <= sqrt(x); i++) {
        if (x % (i * i) == 0) return false;    
        //判断是不是单纯质因数,因为合数中所有的质因数互不相同
    }
    for (int i = 2; i <= sqrt(x); i++) {        
    //判断是不是素数
        if (x % i == 0) return true;
    }
    return false;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(), cout.tie();
    cin >> n;
    for (int i = 1; i <= n; i++) {
        if (judge(i)) cout << i << " ";
    }
}

最大价值

题目描述

昨晚小明做了一个梦,梦中他来到了一个花园,当他打开花园的大门时,小明被眼前的景色惊呆了!他从未见过这么多珍奇的鲜花,还有他梦寐以求的在阳光下发出异样光彩的水晶珠,他慢慢向前走着,忽然被一小块花圃吸引住了。这块花圃中,满是各式各样闪闪发亮的字母水晶珠,小明心想要是能从这花圃中取出自己想要的字母水晶珠串成项链,那该多好啊!小明往右手边一看,有一个告示牌,上面写着:亲爱的朋友,如果你想从花圃中获取字母水晶珠,必须先完成如下游戏。游戏规则如下:假设所有的字母水晶珠都可以采,每种字母水晶珠的单颗价值都是一样的,且采摘任意一颗水晶珠所需要的时间都是1秒,但是采摘水晶珠的总时间是有限的,那么应该如何选择自己所要的字母水晶珠,使得总价值最高呢?我们把每种字母水晶珠的数量和总价值用(A,B)表示,其中A表示这种水晶珠总的数量,B表示所有这种水晶珠的总价值,例如一共有3种字母水晶珠,则其数量和价值分别如下:(4,20)、(4,24)、(5,38),留给你采摘水晶珠的总时间是10秒,当你选择后两种水晶珠全部摘取、第一种摘取一颗时,可得摘取的最大价值为67.00,如果你选择摘前两种水晶珠和两颗第三种水晶珠时,则所得价值为59.20。
小朋友,你能帮助小明计算出给定时间内所能采摘水晶珠的最大价值,从而让他顺利进入花圃采摘字母水晶珠吗?

输入

共N+1行,第一行为两个整数N(5≤N≤100)和T(1≤T≤500)(中间用空格隔开),分别表示字母水晶珠种类数和总的采摘时间;后面N行中,每行两个整数S(1≤S≤50)和V(1≤V≤100)(中间用空格隔开),分别表示这种字母水晶珠的总数量和总价值。

输出

只有一行,表示所能得到的最大价值,结果保留两位小数。

测试样例

样例输入
3 10
4 20
4 24
5 38
样例输出
67.00

做题思路:

在这里插入图片描述

WA代码(自己写的)

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
struct sj{
    int num,value;
    float pare;
}s[105];
int main() {
    int n, t, c = 0;
    float a[105] = {0}, sum = 0;
    cin >> n >> t;
    for (int i = 0; i < n; i++) {
        cin >> s[i].num >> s[i].value;
        s[i].pare = (float) s[i].value / (float) s[i].num;
        a[c++] = s[i].pare;
    }
    sort(a, a + c);
    for (int i = c - 1; i >= 0; i--) {
        for (int j = 0; j < n; j++) {
            if (s[j].pare == a[i]) {
                if (t >= s[i].num) {
                    t -= s[i].num;
                    sum += (float) s[i].value;
                } else {
                    sum += (float) s[i].pare * t;
                    t = 0;
                }
            }
            if (t == 0) {
                printf("%.2f", sum);
                return 0;
            }
        }
    }
}

AC 代码

#include <iostream>
#include <algorithm>
using namespace std;
struct z{
    double a,b,c;
}p[105];
bool cmp(z d,z f){
    return d.c>f.c;
}
int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%lf %lf",&p[i].a,&p[i].b);
        p[i].c=p[i].b/p[i].a;
    }
    sort(p,p+n,cmp);
    double sum=0;
    for(int i=0;i<n;i++){
        m-=(int)p[i].a;
        if(m>=0){
            sum+=p[i].b;
        }else{
            sum+=p[i].c*(m+p[i].a);
            break;
        }
    }
    printf("%.2lf",sum);
    return 0;
}

现在努力攻克前缀和差分!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值