程序设计语言综合设计(第 4 章)

  • 第 4 章
    • 4_ticket
    • 4_flipping_game
    • 4_ogo_planet
    • 4_magic_numbers
    • 4_periods
    • 4_insert
    • 4_dislike

第 4 章

4_ticket

实验任务

  hbb 学长受邀去帮电影《神奇犇犇在哪里》卖门票,售票口有n个人排队等待买票。已知每张门票售价25 元,排队的每个人手上只有25, 50, 100 面值的钞票。一开始hbb 学长在售票处所拥有的钱为0(没有多余的零钱),且n 个人必须按顺序买票。请你判断hbb学长能否成功卖出所有门票。

数据输入

  第一行一个n (1 ≤ n ≤ 100)。表示数组元素个数。
  第二行n 个整数,表示数组a1, a2, …, an。(ai =25,50,100)

数据输出

  输出YES 或者NO。

样例

输入示例

4
25 25 50 50

输出示例

YES

输入示例

2
25 100

输出示例

NO

Code

#include 
int main (void)
{
    short n,i;
    short a[2]={0};
    scanf("%hd",&n);
    for (i=1;i<=n;i++)
    {
        short t;
        scanf("%hd",&t);    //读取支付面额
        switch (t)
        {   //相应面额的张数+1,因为找钱用不到100面额,所以不计算100面额张数
            case 25  :a[0]++; break;    //25面额张数+1
            case 50  :a[1]++; break;    //50面额张数+1
        }
        short money=t-25;   //money为应找钱数
        switch (money)
        {
            case 25 :   //应找25元,则只有一种方案
            {
                if (a[0]<1) //如果25面额不足1张,则无法找零
                {
                    printf("NO\n"); return 0;
                }
                else
                    a[0]--; //如可以找零,则25面额张数-1
            }
            break;
            case 75:    //应找75元,则有两种方案:1、50+25;2、25*3
            {   //有50的情况下先给50,25尽量留着,因为上面一种情况需要用到25
                if (a[1]>0 && a[0]>0)   //若有一张50,一张25,则可找零
                {
                    a[1]--; a[0]--;
                }
                else if (a[0]>2)    //若有三张25,则可找零
                    a[0]-=3;
                else    //否则,不可找零
                {
                    printf("NO\n"); return 0;
                }
            }
            break;
        }
    }
    printf ("YES\n");


    return 0;
}

4_flipping_game

实验任务

  浴盆学长喜欢玩滑来滑去的游戏。他写下n 个数的数组a,a1, a2, …, an。每个数不是0 就是1。他可以做一次操作:给定i 和j(1 ≤ i ≤ j ≤ n),把数组区间[i, j]中的所有数ak 变成1-ak(i≤k≤j)。他想考一考负一号学长,该怎样选择i 和j 才能使操作之后的数组中的1 的数量最多。但是负一号学长觉得这题太简单了,他懒得秒了它,所以就只能由你来和浴盆学长玩这个游戏了。

数据输入

  第一行一个n (1 ≤ n ≤ 100)。表示数组元素个数。
  第二行n 个数,表示数组a1, a2, …, an。

数据输出

  输出一次操作能得到的最大的1 的个数。

样例

输入示例

5
1 0 0 1 0

输出示例

4

输入示例

4
1 0 0 1

输出示例

4

Hint

  样例1:i=2,j=3 或i=2,j=5
  样例2:i=2,j=3


Code

#include 
int main (void)
{
    short n,i;
    scanf("%hd",&n);
    short min=0,num=0,one=0;    //min为子序列和最小值,num为子序列和,one为1的个数
    for (i=1;i<=n;i++)
    {
        short t;
        scanf("%hd",&t);    //读取数字
        if (t)      //如果数字是1
        {
            num++; one++;   //子序列和+1,1的个数+1
        }
        else
            num--;      //否则,子序列和-1
        if (num//如果子序列和小于最小值
            min=num;    //最小值更新
        else if (num>0) //如果子序列和>0
            num=0;      //则清零子序列和
    }
    if (min==0) printf("%d\n",n-1); //对于n个数全是1的情况,因为至少进行一次操作,所以输出n-1
    else printf("%d\n",one-min);    //其他情况,输出1的个数减去最小值(最小值<0)
    return 0;
}

此题把0看作是-1,即可当作是求n个数中子序列和的最小值,如:

0 序列和= -1, 将整个序列反转:1 ,则1的个数可+1(即-(-1))

0 1 序列和= 0, 将整个序列反转:1 0 ,则1的个数可+0(即-(0))

0 1 0 序列和= -1, 将整个序列反转:1 0 1 ,则1的个数可+1(即-(-1))

0 0 1 0 0 序列和= -3, 将整个序列反转:1 1 0 1 1 ,则1的个数可+3 (即-(-3))

因而,子序列和越小,将这个子序列反转之后,1的个数就可加得越多。

寻找最小子序列和,则使用一重循环,当前序列和加上下一个数(0当作-1),若序列和小于最小值,则更新最小值,若序列和大于0,说明此序列再减1不如直接从0减1,即清零子序列,序列起点从下一个数开始。

但还有一个值得注意的点,题目要求至少要做一次操作,所以像

1
1

这样的输入数据,输出应该是0而不是1 。同理,对于全是1的n个数,至少进行一次操作,即把其中一个1变为0是最优解,所以输出应为n-1。


4_ogo_planet

实验任务

  Ogo 星球的人当然说的是ogo 语言。Ogo 语言只由ogo,ogogo,ogogogo,…..等单词组成(这些单词也被称为ogo 单
词)。现在负一号学长被安排去给ogo 星球的军队做文件加密工作,这项工作的内容是把一段字符串中的ogo 单词全部替换成*。负一号学长觉得这些超级简单,所以他懒得做,你能帮负一号学长完成任务吗?

数据输入

  第一行输入一个n(1≤n≤100),表示字符串的长度。
  第二行输入一个字符串(只由小写字母组成)。

数据输出

  输出转换后的字符串。

样例

输入示例

7
aogogob

输出示例

a***b

输入示例

15
ogogmgogogogogo

输出示例

***gmg***

输入示例

9
ogoogoogo

输出示例

*********

Hint

  第一个样例中的ogo 单词是ogogo,第二个样例中的ogo 单词是ogo和ogogogogo。


Code

#include 
int main (void)
{
    short n;
    char s[110];
    scanf("%hd",&n);
    scanf("%s",s);
    short i=0;  //记得初始化
    while (i//从头遍历
    {   //当找到“ogo”时,输出“***”,且i+3
        if (s[i]=='o' && s[i+1]=='g' && s[i+2]=='o')
        {
            printf("***");
            i+=3;
            while (s[i]=='g' && s[i+1]=='o')    //看“ogo”后面有没有“go”
                i+=2;   //有的话i+2
        }
        else
        {   //如果不是“ogo”,原样输出,且i+1
            printf("%c",s[i]);
            i++;
        }
    }
    printf("\n");
    return 0;
}

4_magic_numbers

实验任务

  尾行学姐喜欢由1, 14, 144 组成的数,比如14144, 141414 和1411 是她喜欢的数,但是1444, 514 和414 不是。给出一个数,请你判断尾行学姐喜不喜欢这个数。

数据输入

  第一行输入一个n(1≤n≤10 9 )。

数据输出

  如果尾行学姐喜欢这个数,输出YES,否则输出NO。

样例

输入示例

114114

输出示例

YES

输入示例

1111

输出示例

YES

输入示例

441231

输出示例

NO

Code

#include 
int main (void)
{
    char s[15];
    scanf("%s",s);
    short i=0;
    while (s[i]!='\0')
    {
        if (s[i]=='1')
        {   //如果是‘1’,i+1
            i++;
            if (s[i]=='4')  //‘1’后面如果是‘4’,i再+1
            {
                i++;
                if (s[i]=='4')  //‘4’后面如果是‘4’,i再+1
                    i++;
            }
        }
        else
        {   //否则,直接输出‘NO’
            printf("NO\n");
            return 0;
        } 
    }
    printf("YES\n");
    return 0;
}

4_periods

实验任务

  浴盆学长喜欢玩序列,他想找出一个序列中具有周期性的元素和它的周期。负一号学长决定帮浴盆学长找,但是这题实在是太简单了,负一号学长懒得秒了它,现在就只能由你来做了。现在你要在一组数的序列{a1, a2, …, an}中找到所有符合下列条件的元素x:
  1,该元素x 是{a1, a2, …, an}中的一个数;
  2,该元素x 在序列中重复出现,且该元素重复出现的相邻间隔全都相同。若该元素x 只出现一次,则假设它的间隔为0。
  3,符合条件2 的元素间隔,即为元素x 的周期y。

数据输入

  第一行一个n (1 ≤ n ≤ 100)。表示数组元素个数。
  第二行n 个整数,表示数组a1, a2, …, an。(1 ≤ ai ≤ 100000)

数据输出

  输出m。表示有m 个元素有周期性。
  以下m 行,每行输出两个数x, y,表示元素x 的周期是y。(请按照x 的大小升序输出答案。)注意如果元素只出现一次,也具有周期性,且周期是0。

样例

输入示例

6
1 2 2 1 1 2

输出示例

0

输入示例

8
1 2 1 3 1 2 1 5

输出示例

4
1 2
2 4
3 0
5 0

Hint
  1,“1”重复出现了三次,但它们之间的间隔分别为3 和1,间隔不一致,所以“1”没有周期性。“2”重复出现了三次,它们之间的间隔分别为1 和3,所以“2”也没有周期性。综上,序列中不存在任何元素具有周期性。
  2,“1”重复出现了四次,间隔均为2,所以“1”的周期为2;“2”重复出现了两次,间隔为4,因此周期为4;“3”和“5”都只出现了一次,因此根据题目假设,周期为0。


Code

#include 
#include 
#include 
using namespace std;
struct aa
{
    int value;  //这个数字的值
    short site; //这个数字上次出现的位置
    int period; //这个数字的周期,当period=-1时表示这个数字不具周期性
};
aa a[110];      //a[]记录可能具有周期性的数字
int cmp(aa a1,aa a2)    //sort排序函数
{
    return a1.value//以成员value作为排序标准,升序排序
}

int main (void)
{
    short n,m=0,no_m=0;     //m为出现的数字,no_m为不具周期性的数字
    short i,j;
    scanf("%hd",&n);
    for (i=1;i<=n;i++)
    {
        int t;
        scanf("%d",&t);
        short ok=1; //ok表示这个数字t是否已被a[]记录过
        for (j=1;j<=m;j++)  //循环所有已记录过的数字
        {
            if (a[j].value==t)  //如果数字t已记录过
            {
                if (a[j].period!=-1)    //如果这个数字不是非周期性数字
                {
                    if (a[j].period==0) //如果这个数字之前只出现一次
                    {
                        a[j].period=i-a[j].site;    //那这个数字周期设为i减去之前出现的位置
                        a[j].site=i;    //更新数字最后出现的位置
                    }
                    else if (a[j].period==i-a[j].site)  //如果这个数字的周期不变
                        a[j].site=i;    //更新数字最后出现的位置
                    else    //如果这个数字的周期改变
                    {
                        a[j].period=-1; //这个数字不具周期性
                        no_m++; //不具周期性数字个数+1
                    }
                }
                ok=0;   //数字t已被记录过
            }
        }
        if (ok) //如果数字t没被记录过
        {
            m++;    //记录数字个数+1
            a[m].value=t;   //记录这个数字的值
            a[m].site=i;    //记录这个数字最后出现的位置
            a[m].period=0;  //首次出现,周期为0
        }

    }
    sort(a+1,a+m+1,cmp);    //对所有记录过的数字以value为标准进行升序排序
    printf("%d\n",m-no_m);  //输出具有周期性数字的个数
    for (i=1;i<=m;i++)
        if (a[i].period!=-1)    //如果这个数字具有周期性
            printf("%d %d\n",a[i].value,a[i].period);

    return 0;
}

4_insert

实验任务

  负一号学长总是玩奇怪的游戏。今天他玩的游戏叫做insert。Insert 游戏是这样的:给出一个数组a[1~n](a 数组是1~n 的一种排列),负一号学长每次取出a 数组的最后一个元素,再把它插入到a数组中。请问你,负一号学长最少要执行几次操作,才能把a 数组变成b 数组呢?

数据输入

  第一行一个n (1 ≤ n ≤ 100)。表示数组个数。
  第二行n 个数,表示数组a1, a2, …, an。
  第三行n 个数,表示数组b1, b2, …, bn。

数据输出

  输出最少操作次数。

样例

输入示例

3
3 2 1
1 2 3

输出示例

2

输入示例

5
1 5 2 3 4
1 2 3 4 5

输出示例

3

Hint

样例2 操作过程如下

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

Code

#include 
int main (void)
{
    short n,i,j=1;
    scanf("%hd",&n);
    short a[110],b[110];
    for (i=1;i<=n;i++)
        scanf("%hd",&a[i]);
    for (i=1;i<=n;i++)
        scanf("%hd",&b[i]);

    i=1;    //i和j都从1开始
    short step=0;   //步数
    while (j<=n)
    {
        if (a[i]!=b[j]) //如果a[i]和b[j]不相等
        {
            j++;    //j递增
            step++; //步数+1
        }
        else    //如果两者相等,i和j都+1
        {
            i++; j++;
        }
    }
    printf("%d",step);
    return 0;
}

从a末尾去元素插入到数组a中,其实和从b数组中拿一个元素放到数组b末尾是一样的,所以就看b数组中哪些数字要拿到末尾,使得最终转换成a数组。

于是用两个指针 i 和 j 指向数组a的第 i 位和数组b的第 j 位,若两者相等,i 和 j 都+1,当两者不相等时,j 一直递增,直到b[j]等于a[i],则b[j]到b[i]之间的数字都需要拿到末尾,数字个数即为完成这个过程的步数,之后 i 和 j 再都+1,以此类推。


4_dislike

实验任务

  盆盆学长不喜欢浮点数。他现在有一个大小为2n 的数列,数列元素都是浮点数(保留到小数点后三位)。盆盆学长决定把这个数列的元素都变成整数。变换规则是:取出n 个数向下取整,剩下n 个数向上取整。(例:2.5 向上取整是3,向下取整是2)但是如果变换之后的数列的和与变换前数列的和相差太多,盆盆学长会伤心。请你找出变换前后和相差最小的方案,并输出相差了多少(保留到小数点后三位)。

数据输入

  第一行一个n (1 ≤ n ≤ 100)。表示数组元素个数。
  第二行2n 个整数,表示数组a1, a2, …, an。(0 ≤ ai ≤ 10000)

数据输出

  输出ans(保留到小数点后三位)。

样例

输入示例

3
0.000 0.500 0.750 1.000 2.000 3.000

输出示例

0.250

Hint

  | (0 + 0.5 + 0.75 + 1 + 2 + 3) - (0 + 0 + 1 + 1 + 2 + 3) | = 0.25


Code

#include 
#include 
int main (void)      
{
    short n;
    scanf("%hd",&n);
    short nn=2*n,i,one=0;   //one为小数部分为.000的数的个数
    double ans=0.0;
    for (i=1;i<=nn;i++)
    {
        double t;
        scanf("%lf",&t);
        if ( fabs(fmod(t,1)-0.0) < 1e-8 )   //fmod为浮点数求余,浮点数作差判断是否相等
            one++;  //若小数部分为.000,one+1
        ans+=fmod(t,1); //ans为所有小数部分之和
    }
    double min=200.0,temp;
    if (one<=n) //如果小数部分为.000的数的个数小于等于总数nn的一半
        for (i=n-one;i<=n;i++)  //-1的个数范围从n-one到n
        {
            temp=ans-i;
            if ( fabs(temp) < min )
                min=fabs(temp); //枚举得到最小值
        }
    else    //如果小数部分为.000的数的个数大于总数nn的一半
        for (i=0;i<=nn-one;i++) //-1的个数范围从0到nn-one
        {
            temp=ans-i;
            if ( fabs(temp) < min )
                min=fabs(temp);
        }
    printf("%.3f\n",min);   //保留三位小数输出
    return 0;    
}

若一个数小数部分为x,

  • 将其向下取整,则 原来的数 - 现在的数 = x
  • 将其向上取整,则 原来的数 - 现在的数 = -(1-x) = x-1

则可得出结论,在不存在小数部分为.000的数时,对所有的2n个数,无论向上取整还是向下取整,

  • 原数之和 - 现数之和 = n*x - n*1

而若存在小数部分为.000的数时,因为这样的数向上取整之后,原数 - 现数 = 0.000 = x ,并不会再 -1,所以可根据将 小数部分为.000的数 向上取整的个数来调节-1的个数

  • 若小数部分为.000的数的个数one小于等于n

    则可向上取整的 小数部分非.000的数 的个数最少为n-one ,最大为n,即-1的个数为最少为n-one ,最大为n

  • 若小数部分为.000的数的个数one大于n

    则可向上取整的 小数部分非.000的数 的个数最少为0 ,最大为nn-one,即-1的个数为最少为0 ,最大为nn-one

最后枚举得出绝对值最小的值。


你可能感兴趣的:(题解)