openjudge程设实习2023期末答案(A-D)

A:围栏 

​​​​​​​

总时间限制: 1000ms

单个测试点时间限制: 100ms

内存限制: 65536kB

描述

小 A 打算新建一幢楼,这幢楼需要占用一块长方形的面积恰好为 n 平方米的土地,且为了方便测量,这块土地的长宽必须为整数米。

小 A 需要在这幢楼外边修围栏,围栏长度为这块长方形的周长。

现在想要知道最小的围栏长度。

输入

第一行一个正整数 n(n ≤ 2 × 10^9),表示楼的面积。

输出

一行一个数表示答案。数据保证答案在int范围内。

样例输入

2

样例输出

6

提示

这是一道简单的枚举题,枚举长方形的短边即可,即枚举1到根号n就可以。
最后答案在int范围内,但注意周长中间结果可能会超过int

代码:

#include <iostream>
using namespace std;

int main(){
    int n;
    cin >> n;
    int halflength=n+1;
    for (int i=1;i*i<=n;++i){
        if (n%i==0){
            halflength=i+n/i;
        }
    }
    cout << 2*halflength;
}

B:解密

总时间限制: 1000ms

单个测试点时间限制: 100ms

内存限制: 65536kB

描述

有一种简单的加密算法,对于一个长度为n的字符串,这个算法将会以第(n+1)/2(向下取整)个字符为中间轴(最左边的字符算第1个字符),将该字符写在密文的开头,然后对左半部分按照同样的办法进行加密并写下密文,再对右半部分按照同样的办法进行加密并写下密文。以此类推,直到左右部分为空,即完成加密。

例如,如果要对12345678进行加密,第一步将选择4作为中间轴,将其写在密文开头,然后继续对左右两边(123和5678)分别继续按这个算法处理并写下,我们可以将其记作4[123][5678]([]代表待加密处理的部分)。

对于左半部分123,中间轴是2,左半部分为1,右半部分为3因此加密结果为213(1的中间轴为1,左右均为空,因此结果为1,而3同理)。

对于右半部分5678,中间轴是6,左半部分为5,右半部分为78,因此加密结果为65[78] → 6578(78的中间轴为7,左半部分为空,右半部分为8,因此得到78)。

简单来说,整个加密过程如下:

12345678 → 4[123][5678] → 42[1][3][5678]→ 4213[5678] → 42136[5][78] → 42136578

因此,对12345678的加密结果为42136578。

现在给出一个长度为n(1 ≤ n ≤  50000)的由数字构成的字符串,这个字符串是加密后的密文,请你还原出加密前的明文。

输入

一行,一个长度为n(1 ≤ n ≤ 50000)的由数字构成的字符串字符串,代表加密后的密文。

输出

一行,一个长度同样为n的字符串,代表解密后的明文。

样例输入

123456789

样例输出

324517689

提示

324517689 → 1[3245][7689] → 12[3][45][7689] → 12345[7689] → 123456[7][89] → 123456789

分析

一道简单的递归题,考虑反向过程即可,注意递归时尽量在原有的string上直接调位置,不然很容易超时

代码:

#include <iostream>
#include <string>
using namespace std;

string s;
char t;
void process(int start,int end){
    int n=end-start;
    if (n<=2){
        return;
    }
    int loc=(n+1)/2;
    t=s[start];
    for (int i=start;i<start+loc-1;++i){
        s[i]=s[i+1];
    }
    s[start+loc-1]=t;
    process(start,start+loc-1);
    process(start+loc,end);
}

int main(){
    cin >> s;
    int n=s.length();
    process(0,n);
    cout << s;
}

C:传送法术

总时间限制: 1000ms

单个测试点时间限制: 100ms

内存限制: 65536kB

描述

小明在一个大小为 1 × n 的迷宫中,坐标范围为0,1,2...,n-1,maze[i] 表示迷宫第i格的地形:

• 若为 . ,表示可以到达的空地;

•  若为 # ,表示不可到达的墙壁;

• 若为 S ,表示小明的起点位置;

•  若为 T ,表示迷宫的出口位置。

小明每次可以向 左、右 相邻的位置移动一格。

此外,小明还有一个可以无限使用的传送法术,使用后会被传送到镜像的位置。

具体的,当小明在x的位置使用传送法术,会被传送到n-x-1的位置。例如,当n=5时,如果小明在0位置使用传送法术,会被传送到4的位置。

移动或使用法术都需要一步,现在小明想知道,最少几步它可以从起点走到迷宫的出口?

**注意:**

•  如果传送后的位置是墙壁,则不能使用传送法术

•  传送后的位置可能与原位置相同

输入

第一行包含一个整数 n,表示迷宫的长度。(2 ≤ n ≤ 1000)
第二行包含一个长度为 n 的字符串maze,表示迷宫。

输出

一个整数,表示小明从起点走到出口最少的步数。如果无法到达,输出-1

样例输入

11
S#...#T#...

样例输出

7

分析

状态表示简单(只有位置一个变量,且每个点至多只会被走1次)的最优步数问题,适合广搜算法。

代码:

#include <iostream>
#include <string>
using namespace std;

int main(){
    int n;
    cin >> n;
    string s;
    cin >> s;
    int map[n];
    int a[n+1];
    int b;
    for (int i=0;i<n+1;++i){
        a[i]=-1;
    }
    for (int i=0;i<n;++i){
        if (s[i]=='S'){
            map[i]=0;
            a[0]=i;
        }
        if (s[i]=='#'){
            map[i]=-1;
        }
        if (s[i]=='.'){
            map[i]=10000;
        }
        if (s[i]=='T'){
            b=i;
            map[i]=10000;
        }
    }
    int current=0;
    int savepoint=1;
    while (a[current]!=-1){
        if (a[current]==b){
            cout << map[a[current]];
            break;
        }
        int i=a[current];
        if (map[i]+1<map[n-i-1]){
            map[n-i-1]=map[i]+1;
            a[savepoint]=n-i-1;
            savepoint+=1;
        }
        if (i!=n-1){
            if (map[i]+1<map[i+1]){
                map[i+1]=map[i]+1;
                a[savepoint]=i+1;
                savepoint+=1;
            }
        }
        if (i!=0){
            if (map[i]+1<map[i-1]){
                map[i-1]=map[i]+1;
                a[savepoint]=i-1;
                savepoint+=1;
            }
        }
        current+=1;
    }
    if (a[current]==-1){
        cout << -1;
    }
}

D:购买优惠券

总时间限制: 7400ms

单个测试点时间限制: 200ms

内存限制: 65536kB

描述

在超市的货架上有n个商品,每个商品都有一个价值ai。小明有m张优惠券,可以使用这些优惠券购买货架上连续的总价值不超过优惠券面值的若干商品。根据特殊的规定,小明必须购买m张面值相同的优惠券,而且优惠券面值越高,价格越贵。请帮助小明计算,如果他需要购买所有商品,最少需要购买的优惠券面值是多少。

优惠券面值必须是整数,且一个商品不能用两张优惠券凑起来购买。比如一张优惠券购买了若干商品后还剩2元,然而下一个商品价格超过2元,那么这张优惠券剩下的2元只能作废。

请注意:所有商品价格之和可能超过int表示范围,所以答案也可能超过int表示范围。该用long long 的地方就要用long long。

输入

第一行两个整数n,m,分别表示商品的数量(1≤ n ≤ 10^5)和要分成的组数(1≤ m ≤ n)。
第二行n个整数a1,a2,...,an,表示每个商品的价值 (1 ≤ ai ≤ 10^5)

输出

一个整数,表示最少需要购买的优惠券面值

样例输入

7 3
2 5 1 9 3 6 2

样例输出

11

分析

看似动态规划,实则每个优惠券面值都可以在O(n)的时间内验证够不够,因此只需要二分法查找最小的面值即可(类似河中跳房子)

代码:

#include <iostream>
using namespace std;

int main(){
    long long n,m;
    cin >> n >> m;
    long long a[n];
    long long total=0;
    for (int i=0;i<n;++i){
        cin >> a[i];
        total+=a[i];
    }
    long long max=total;
    long long min=total/m-1;
    while (max-min>1){
        long long avg=(max+min)/2;
        long long ticket=m;
        long long remainprice=0;
        long long i=0;
        while (ticket>-1 & i<n){
            if (remainprice < a[i]){
                ticket-=1;
                remainprice=avg;
            }
            else {
                remainprice-=a[i];
                i+=1;
            }
        }
        if (ticket<=-1){
            min=avg;
        }
        else {
            max=avg;
        }
    }
    cout << max;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值