C语言实践第四次习题集

特殊的翻译

题目

小明的工作是对一串英语字符进行特殊的翻译:当出现连续且相同的小写字母时,须替换成该字母的大写形式,在大写字母的后面紧跟该小写字母此次连续出现的个数;与此同时,把连续的小写字母串的左侧和右侧的字符串交换位置;重复该操作,直至没有出现连续相同的小写字母为止。现在小明想请你帮他完成这种特殊的翻译。

输入格式:

输入一串由小写字母构成的字符串。(字符串长度不大于250)

输出格式:

输出翻译后的字符串。

输入样例1:

dilhhhhope

输出样例1:

opeH4dil

输入样例2:

lodnkmgggggggoplerre

输出样例2:

eG7lodnkmR2ople

思路

此题需要注意的其实就是小写字母连续重复的次数需要考虑大于9的情况(之前没考虑这个,在这里卡了很久呜呜呜),还有就是对字符串的操作(对检测到的满足条件的序列的起点和终点的标记)。

完整代码

#include<stdio.h>
#include<string.h>
void f(int first,int end);
char str[300],temp[300];
int flag;
int main()
{
    int i,j,first,end;
    scanf("%s",str);
    i=0;
    while(str[i]!='\0'){
        flag=0;
        if('a'<=str[i]&&str[i]<='z'&&str[i]==str[i+1]){
            first=i;
            j=i;
            do{
                j++;
            }while(str[j]==str[j+1]);
            end=j;
            f(first,end);
        }
        else i++;
        if(flag==1) i=0;
    }
    printf("%s",str);
    return 0;
}
void f(int first,int end)
{
    int i,j,gap;
    for(i=end+1,j=0;str[i]!='\0';i++){//把后一段字符串储存到另一个数组的前部
        temp[j++]=str[i];
    }
    temp[j++]=str[first]-32;
    gap=end-first+1;
    while(gap/10!=0){
        temp[j++]=gap/10+'0';
        gap=gap%10;
    }
    temp[j++]=gap+'0';
    for(i=0;i<first;i++){//把前一段字符串储存到另一个数组的后部
        temp[j++]=str[i];
    }
    temp[j]='\0';
    strcpy(str,temp);
    flag=1;
}

好吃的巧克力

题目

超市正在特价售卖巧克力,正好被贪吃的Lucky_dog看见了。

巧克力从左到右排成一排,一共有N个,M种。

超市有一个很奇怪的规定,就是你在购买巧克力时必须提供两个数字a和b,代表你要购买第 a 个至第 b 个巧克力(包含 a 和 b)之间的所有巧克力。

假设所有巧克力的单价均为1元。Lucky_dog想吃所有种类的巧克力,但又想省钱。作为Lucky_dog的朋友,他请你来帮他决定如何选择购买巧克力时的 a 和 b。

输入格式:

第一行包含两个正整数 N 和 M(M<=N, N<=10^6 , M<=2000),分别代表巧克力的总数及种类数。

第二行包含 N 个整数,这些整数均在1 至 M 之间,代表对应的巧克力所属的种类。

输出格式:

输出仅一行,包含两个整数a和 b(a<=b) ,由一个空格隔开,表示花费最少且包含所有种类巧克力的购买区间。

数据保证有解,如果存在多个符合条件的购买区间,输出a最小的那个。

输入样例:

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

输出样例:

2 7

思路

大佬题解
完成这题需要知道如何搜寻最优解。题目要求的不仅是能吃到所有种类巧克力的方案,而且还需要最省钱。因为只需求最优解,因此可以选用擂台法来筛选出最优解。

可以先找到满足条件的第一种情况:

for(i=0;sum<M;i++){
     if(k[n[i]]==0){
       sum++;
     }
     k[n[i]]++;
     s1++;
}

然后将代表巧克力方案的序列(起点和终点之间的方案序列)往后推,即去掉序列头元素,接着检查此时序列是否仍能满足包含所有种类巧克力,若不能满足,说明需要从末尾继续向后寻找,直至找到相应种类的巧克力补上,或者超出所有巧克力数(搜索到达尽头),此过程用循环来完成,每次循环完成一次擂台对比,看看是否需要更新最优解。

完整代码

#include<stdio.h>
int k[2005]={0},n[1000005];//k数组用来记录每一种巧克力当前的数量
int main()
{
    int i,M,N,sum=0,s1=0,s2,start,last,first,end,store,flag;
    scanf("%d %d",&N,&M);
    for(i=0;i<N;i++) scanf("%d",&n[i]);
    for(i=0;sum<M;i++){//第一种情况 
        if(k[n[i]]==0){
            sum++; 
        }
        k[n[i]]++;//计算各种巧克力的个数 
        s1++;//巧克力总数 
    }
    first=0;//标记此时最优解序列的起点 
    start=0;//标记当前序列的起点 
    last=i-1;//标记此时最优解序列的终点
    end=i;//当前序列末尾+1 
    s2=s1;//s1为最优解的巧克力数量,s2为当前序列的巧克力数量 
    while(end!=N){
        flag=1;//标记判断是否越界 
        s2=s2-1;//s2为前一次方案所花的钱数
        k[n[start]]--;//把前一种的首个巧克力去掉 
        if(k[n[start]]==0){//需要往后补充 
            sum--;
            for(i=end;sum<M&&i<N;i++){
                if(n[i]==n[start]){
                    sum++;
                }
                k[n[i]]++;
                s2++;
            }
            if(sum!=M) flag=0;
            end=i;
        }
        if(flag==0) break;//越界,直接结束循环 
        start++;
        if(s2<s1){//保证s1为目前最优解的巧克力总数 
            s1=s2;
            first=start;
            last=end-1;
        }
    }
    printf("%d %d",first+1,last+1);
    return 0;
}

下次一定(续)

题目

你是一个bilibili的六级号,由于经常一键三连,所以一个硬币都没有,现在你又做了个梦,在梦中你制定了一个硬币增加规则:

第一天登陆后硬币总数1个,第二天登陆后硬币总数112个,第三天登陆硬币总数112123个......,以此类推,梦中不知日月,你瞬间拥有了11212312341234512345612345671234567812345678912345678910123456789101112345678910......无穷多个硬币。

常年维护B站秩序的百漂怪看不下去了,决定随机挑一个数x,作为这个无穷数的第x位,如果你能正确答对这第x位上的数是什么,就赠送给你与这个数等量的硬币。

百漂怪总共会挑t次。

你决定编程模拟一下,搏一搏,单车变摩托。

输入格式:

第一行包含一个正整数t(1≤t≤10),表示百漂怪挑了t次。 接下来t行,每行一个正整数x (1 ≤ x≤ 2^31-1),表示第i次,百漂怪挑的是第x位。

输出格式:

输出共t行,每行输出对应第x位上的数。

输入样例1:

2
3
8

输出样例1:

2
2

输入样例2:

6
222
66666
99999999
66656565
22222
2

输出样例2:

6
4
9
4
1
1

思路

这题有一个测试点不管我怎么改,总是超时(我太难了呜呜呜),有无大佬来救救蒟蒻啊。
对于本题,我第一个想法是直接用字符数组完整模拟构造那个大数,然后根据输入的位数逐一输出即可。
但后面发现还是不好操作。
然后我又想了一个算法。通过观察,我们不难发现那个数的构造规律,举例说明的话:1->1 12->1 12 123 ,不难看出,112123比112多了1,2,3,而3是由2+1得到的,可推得接下去应该是1 12 123 1234
4由3+1得到,需要特别注意的是,之后会出现10,10这个数字需要用两个元素来存储(分成1和0)。
这样分析下来,每次在延伸该数时,必须做两次添加字符操作,如1->112,先将1后添加一个2,组成12字符串,然后把12字符串添加到“1”的后面。
没必要这么做,我们可以把这个数分段看,如1 12 123 1234 12345 123456 1234567 12345678,这样我们可以看到,其实只需要完成第一种添加操作就好了,我们的字符数组只需储存完成第一种添加之后的字符串即可(如12,123,1234......),只不过我们需要及时把需要输出的字符输出。
这样,我们来分析一下第一种的添加操作应该如何实现:我用一个num数组来储存一个标准数,每次将标准数加一,即可得到需要添加的字符,然后将其与前一次的字符串连接起来就行了。
我专门写了一个函数来完成此功能

void add(int len1)
{
    int i,extra=1,sum;
    for(i=0;i<len1;i++){
        sum=num[i]-'0'+extra;
        if(sum>=10){
            num[i]=sum-10+'0';
            extra=1;
        }
        else{
            num[i]=sum+'0';
            extra=0;
        }
        if(extra==0){
            break;
        }
    }
    if(extra==1){
        num[len1++]=1+'0';
    }
    num[len1]='\0';
}

值得注意的是,这里加法的实现还有一个问题,即需要逆序储存数字,这样数字的长度才是可增加的
模拟竖式运算:

//array index   1 2 3 4 5 6 7 8 9
                5 4 3 2 1
            +   7 8 9 9
            -------------
                2 3 3 2 2
//jw            1 1 1 1

//array index是数组下标,jw代表进位

如此的话,形成的字符串也需要是逆序储存才行
针对字符串的添加操作,同时及时储存所需要的值

void cal(int N,int key)
{
    int len1=strlen(num);
    while(count<N){//计算生成的新字符串的终点位置是否达到甚至超过所要求的位置,保证不错过所需位置的数据
        add(len1);
        str=num+str;
        len1=strlen(num);
        tot+=len1;
        count+=tot;
    }
    res[key]=str[count-N];
}

还有一点,在主函数里:

for(i=0;i<t;i++){
        cin>>a[i];
        order[i]=i;
    }
    for(i=1;i<t;i++){
        for(j=0;j<t-i;j++){
            if(a[j]>a[j+1]){
                s=a[j];
                a[j]=a[j+1];
                a[j+1]=s;
                s=order[j];
                order[j]=order[j+1];
                order[j+1]=s;
            }
        }
    }

我把所要求的位数从小到大排,并且用order标记输出顺序(即输入顺序)
这部分的代码是保证在构造那个数的过程中,那些数位可以及时被储存在res数组中的适当位置。

完整代码

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
void cal(int N,int key);
void add(int len);
string str="1";
char num[100000]={"1"};
char res[15];
int count=1,tot=1;
int main()
{
    int t,i,j,a[15],s,order[15];
    cin>>t;
    for(i=0;i<t;i++){
        cin>>a[i];
        order[i]=i;
    }
    for(i=1;i<t;i++){
        for(j=0;j<t-i;j++){
            if(a[j]>a[j+1]){
                s=a[j];
                a[j]=a[j+1];
                a[j+1]=s;
                s=order[j];
                order[j]=order[j+1];
                order[j+1]=s;
            }
        }
    }
    for(i=0;i<t;i++){
        cal(a[i],order[i]);
    }
    for(i=0;i<t;i++) cout<<res[i]<<endl;
    return 0;
}
void cal(int N,int key)
{
    int len1=strlen(num);
    while(count<N){
        add(len1);
        str=num+str;
        len1=strlen(num);
        tot+=len1;
        count+=tot;
    }
    res[key]=str[count-N];
}
void add(int len1)
{
    int i,extra=1,sum;
    for(i=0;i<len1;i++){
        sum=num[i]-'0'+extra;
        if(sum>=10){
            num[i]=sum-10+'0';
            extra=1;
        }
        else{
            num[i]=sum+'0';
            extra=0;
        }
        if(extra==0){
            break;
        }
    }
    if(extra==1){
        num[len1++]=1+'0';
    }
    num[len1]='\0';
}

猜你喜欢

转载自www.cnblogs.com/031902522ycy/p/12537026.html
今日推荐