《程序设计思维与实践》 week8 CSP—M2

A - HRZ的序列

题目描述

相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列a,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数K,使得一些数加上K,一些数减去K,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!

Input

输入第一行是一个正整数t表示数据组数。
接下来对于每组数据,输入的第一个正整数n表示序列a的长度,随后一行有n个整数,表示序列a。

Output

输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)

Sample Input

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

Sample Output

NO
NO

解题思路

满足题意的数组只有三种情况:
(1)数组中只有一种数字;
(2)数组中只有两种数字;
(3)数组中有三种数字a, b, c(升序排列),并且满足 b - a == c - b;

输入序列a[ ],vector< int > s 用于存储出现的数字的种类;
遍历数组a,每出现一种新的元素就将其存入s中,s.size( )实际上就相当于出现的数字的种类;
对满足题意的三种情况做具体判断,当s.size( ) > 3 时,直接结束遍历,输出NO;

解题代码

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>

using namespace std;

int t, n;
long long int a[10005];
vector<long long int> s;
int type;

int main() {
    cin>>t;
    for (int x = 0; x < t; x++) {
        //初始化
        s.clear();
        
        cin>>n;
        //序列a[]的初始化
        for (int y = 0; y < n; y++) {
            long long int number; cin>>number;
            a[y] = number;
        }
        
        //判断
        int symbol = 0;
        for (int y = 0; y < n; y++) {
            if (s.size() == 0) s.push_back(a[y]);
            //不能出现超过三种的数字
            if (s.size() > 3) {symbol = -1; break;}
            else {
                //与已经出现过的数进行比对
                int yon = 0;
                for (long int z = 0; z < s.size(); z++) {
                    //该数匹配成功
                    if (s[z] == a[y]) {yon = 1; break;}
                    //该数未匹配成功
                    else {yon = -1; continue;}
                }
                //该数是一个新数
                if (yon == -1) s.push_back(a[y]);
            }
        }
        if (s.size() == 3) {
            sort(s.begin(), s.end());
            if (s[1] - s[0] != s[2] - s[1]) symbol = -1;
        }
        if (symbol == -1) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
    return 0;
}

B - HRZ学英语

题目描述

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串 ,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。
例如:
输入
AB??EFGHIJKLMNOPQRSTUVWXYZ
输出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。

Input

输入只有一行,一个符合题目描述的字符串。

Output

输出只有一行,如果存在这样的子串,请输出,否则输出-1

Sample Input

ABC??FGHIJK???OPQR?TUVWXY?

Sample Output

ABCDEFGHIJKLMNOPQRSTUVWXYZ

解题思路

从输入的字符串的第一个字符开始选择一段长度为26的字符串,然后将首位置往后移一位;
对选择的长度为26的字符串进行判断:
遍历这个字符串 w_number 存储 ? 的数目; z_number 存储字符的数目;fu[ ]存储已经出现了的字母;
遍历结束之后:
(1)w_number + z_number < 26,那么是不满足条件的;
(2)w_number + z_number == 26,从fu[ ]中获取未出现的字母,用一个队列can按照字典序存储;输出的时候,先输出已有的字母,在对应的 ? 的位置,输出队列can的队首元素;

解题代码

#include <iostream>
#include <string>
#include <queue>

using namespace std;

struct zi {
    long int n; //在输入字符串里的位置
    char m; //字符
}j[26];

int w_number = 0; //?个数
int z_numebr = 0; //字符个数
int fu[26]; //存储要输出字符
queue<char> can;

int main() {
    string z; cin>>z;
    int symbol = 0;
    long int begin;
    //获取一段长度为26的字符串
    for (begin = 0; begin < z.size(); begin++) {
        //判断是否继续
        if (z.size() - begin < 26) break;
        
        //初始化
        for (int a = 0; a < 26; a++) fu[a] = 0;
        w_number = 0; z_numebr = 0;
        while (can.size() != 0) {can.pop();}
        
        //存储这一段字符里面的元素
        for (long int a = begin; a < begin + 26; a++) {
            if (z[a] == '?') w_number++;
            else {
                int q = z[a] - 65;
                if (fu[q] == 1) continue;
                else {
                    fu[q] = 1;
                    j[z_numebr].n = a; j[z_numebr].m = z[a];
                    z_numebr++;
                }
            }
        }
        
        //进行判断
        if (z_numebr + w_number < 26) symbol = -1;
        else {
            //确认空缺的元素
            for (int b = 0; b < 26; b++) {
                if (fu[b] == 0) {
                    char s = 'A' + b;
                    can.push(s);
                }
            }
            
            //输出答案
            if (j[0].n != begin) {
                long int d = j[0].n - begin;
                for (int f = 0; f < d; f++) {
                    cout<<can.front();
                    can.pop();
                }
            }
            for (int f = 0; f < z_numebr; f++) {
                cout<<j[f].m;
                //中间有?号的情况
                if (j[f+1].n != j[f].n + 1) {
                    long int d = j[f+1].n - j[f].n - 1;
                    for (int g = 0; g < d; g++) {
                        cout<<can.front();
                        can.pop();
                    }
                }
            }
            while (!can.empty()) {cout<<can.front(); can.pop();}
            return 0;
        }
    }
    if (symbol == -1) cout<<-1<<endl;
    
    return 0;
}

C - 咕咕东的奇妙序列

题目描述

咕咕东正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课。此时她在睡梦中突然想到了一个奇怪的无限序列:112123123412345…这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。
所以,这个序列的前56项会是11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是5,第38项是2,第56项是0。
咕咕东现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。

Input

输入由多行组成。
第一行一个整数q表示有q组询问(1<=q<=500)
接下来第 i+1 行表示第i个输入 ki ,表示询问第 ki 项数字。( 1<=ki <=10^18)

Output

输出包含 q 行
第i行输出对询问 ki 的输出结果。

Sample Input

5
1
3
20
38
56

Sample Output

1
2
5
2
0

解题思路

用数组a[ i ]存储 i 位数的区间个数, b[ i ] 存储 i 位数的最后一个区间的长度
对于 a[ l ] = ( (b[I - 1] + l ) + (b[l - 1] + 9 x pow(10, l - 1) x l ) ) * 9 x pow(10, l - 1) / 2;

(1)用二分法判断输入的k位于那个区间;
(2)按照1位数占9 · 1位,2位数占 90 · 2位…… 确定k在目标区间的具体位置;
(3)确定k所在位置的数的值,以及具体是第几位;
(4)所在位置数进行除10取余的操作,获得目标的输出值;

解题代码

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <vector>

using namespace std;

int q;
long long int ans;
vector<long long int> a, b;

void initialize () {
    a.push_back(0); b.push_back(0);
    long long int i = 1, all_num = 9;
    while (a[i - 1] <= static_cast<long long int>(pow(10, 18))) {
        //i位数的区间的总数
        long long int x = ((b[i - 1] + i) + (b[i - 1] + all_num * i)) * all_num / 2;
        //i位数的最后一个对应的区间长度
        long long int y = b[i - 1] + all_num * i;
        all_num *= 10;
        a.push_back(x); b.push_back(y);
        i++;
    }
}

long long int get_ans (long long int k) {
    //查找k是几位数
    int l = 1;
    while (k > 0) {k -= a[l]; l++;}
    l--; k += a[l];
    
    //查找是在l-1位数的区间内的哪一个区间
    long long left = 1, right = 9 * static_cast<long long int>(pow(10, l - 1)), mid;
    while (left <= right) {
        mid = (left + right) / 2;
        long long int sum = ((b[l - 1] + l) + (b[l - 1] + mid * l)) * mid / 2;
        if (sum >= k) right = mid - 1;
        else left = mid + 1;
    }
    k -= ((b[l - 1] + l) + (b[l - 1] + right * l)) * right / 2;
    
    //确定k在目标区间的位置
    long long int u = 1;
    while (k > 0) {k -= 9 * static_cast<long long int>(pow(10, u - 1)) * u; u++;}
    u--; k += 9 * static_cast<long long int>(pow(10, u - 1)) * u;
    long long int shang = (k + u - 1) / u;
    long long int yu = k - (shang - 1) * u;
    long long mubiao = static_cast<long long int>(pow(10, u - 1)) + shang - 1;
    for (long long int h = 0; h < u - yu; h++) mubiao = mubiao / 10;
    ans = mubiao % 10;
    return ans;
}

int main() {
    cin>>q;
    initialize();
    while (q--) {
        long long int k; cin>>k;
        cout<<get_ans(k)<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值