• C语言程序设计100例之(30):删数问题


    例30  删数问题

    问题描述

    从键盘输入一个高精度正整数num(num不超过250位),任意去掉S个数字后剩下的数字按原先后次序将组成一个新的正整数。编写一个程序,对给定的num和s,寻找一种方案,使得剩下的数字组成的新数最小。

    输入格式

    num (高精度的正整数)和S(需要删除的数字个数)。

    输出格式

    最后剩下的最小数。

    输入样例

    51428397

    5

    输出样例

    123

            (1)编程思路。

            由于键盘输入的是一个高精度正整数num(num不超过250位),因此用字符串数组来进行存储。

            为了尽可能地逼近目标,选取的贪心策略为:每一步总是选择一个使剩下的数最小的数字删去,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字,否则删除第一个递减区间的首字符。然后回到串首,按上述规则再删除下一个数字。重复以上过程s次,剩下的数字串便是问题的解了。

            也就是说,删数问题采用贪心算法求解时,采用最近下降点优先的贪心策略:即x1<x2<…<xi<xj;如果xk<xj,则删去xj,得到一个新的数且这个数为n-1位中为最小的数N1,可表示为x1x2…xixkxm…xn。对N1而言,即删去了1位数后,原问题T变成了需对n-1位数删去k-1个数的新问题T′。新问题和原问题相同,只是问题规模由n减小为n-1,删去的数字个数由k减少为k-1。基于此种删除策略,对新问题T′,选择最近下降点的数进行删除,如此进行下去,直至删去k个数为止。

            另外,按这个方法删除s位后,要注意去掉结果中可能存在的前导0。

            (2)源程序。

    #include <stdio.h>

    #include <string.h>

    int main()

    {

        char num[251]={''};

        int s,i,j;

        scanf("%s",num);

        scanf("%d",&s);

        while (s>0)    // 循环s次,每次删除一个数字

        {

               i=0;          // 每次删除后从头开始搜寻待删除数字

              while (num[i]!='' && num[i]<=num[i+1])

                    i++;

             for(j=i;j<strlen(num);j++)

                   num[j]=num[j+1];   // 将位置i处的数字删除

            s--;

        }

        i=0;

        while(num[i]=='0') i++;  //处理前导0

        if (num[i]=='') printf("0 ");

        else  printf("%s ",&num[i]);

        return 0;

    }

    习题30

    30-1  删数问题(加强版)

            本题选自洛谷题库 (https://www.luogu.org/problem/ P1323)

    题目描述

    一个集合有如下元素:1是集合元素;若P是集合的元素,则2 * P +1,4*P+5也是集合的元素,取出此集合中最小的K个元素,按从小到大的顺序组合成一个多位数,现要求从中删除M个数位上的数字,使得剩下的数字最大,编程输出删除前和删除后的多位数字。

    注:不存在所有数被删除的情况

    输入格式

    输入的仅一行,K,M的值,K,M均小于等于30000。

    输出格式

    输出为两行,第一行为删除前的数字,第二行为删除后的数字。

    输入样例

    5  4

    输出样例

    137915

    95

            (1)编程思路。

            本题是例30的加强版,主要是先要生成待删除数字的多位数。多位数组成的数字来自给定集合,集合中前30000个元素的生成方法参见“C语言程序设计100例之(14):丑数”中的编程思路。

            生成了多位数后,再按例30的贪心策略进行数字的删除。

            (2)源程序。

    #include <stdio.h>

    int main()

    {

         int H[30001];

         char num[300000]={''},temp[10];

         int k,m,i,j,t,cnt,len;

         scanf("%d%d",&k,&m);

        int p2,p3,min;

       H[1]=1;

        p2=p3=1;

        i=1;

        while(i<k)   

       {

                min=2*H[p2]+1;

                if (min>4*H[p3]+5) min=4*H[p3]+5;

                H[++i]=min;

                if(H[i]==2*H[p2]+1)  p2++;

                 if(H[i]==4*H[p3]+5)  p3++;

         }

        len=0;

        for (i=1;i<=k;i++)

        {

            t=H[i];  cnt=0;

            while (t!=0)

            {

                temp[cnt++]=t%10+'0';

                t/=10;

            }

            for (j=cnt-1;j>=0;j--)

                num[len++]=temp[j];

        }

        num[len]='';

        printf("%s ",num);

        cnt=0;       // 删除掉的数字个数

        i=1, j=0;    // 下标i用于遍历字符串,下标j用于保存结果字符串

        while (i<len && cnt!=m)

        {

            if (num[i]<=num[j])     // 不删除,保留到结果串中

                num[++j]=num[i++];

            else

            {

                j--,cnt++;       // 删除结果串中下标j所指字符

                if (j==-1) num[++j]=num[i++];

            }

        }

        while (i<len) num[++j]=num[i++];

        num[++j]='';

        printf("%s ",num);

        return 0;

    }

    30-2  学生分组

             本题选自洛谷题库 (https://www.luogu.org/problem/ P1109)

    题目描述

    有N组学生,给出初始时每组中的学生个数,再给出每组学生人数的上界R和下界L (L≤R),每次你可以在某组中选出一个学生把他安排到另外一组中,问最少要多少次才可以使N组学生的人数都在[L,R]中。

    输入格式

    第一行一个整数N,表示学生组数; n≤50

    第二行N个整数,表示每组的学生个数;

    第三行两个整数L,R,表示下界和上界。

    输出格式

    一个数,表示最少的交换次数,如果不能满足题目条件输出-1。

    输入样例

    2

    10 20

    10 15

    输出样例

    5

            (1)编程思路。

            输入N组学生每组人数时累加求出学生总人数sum,若sum<N*L(表示分N组,每组最少L人,总人数不足),或sum>N*L(表示分N组,每组最多R人,总人数超出了,有学生无法放入某一组)时,输出“-1”。

            若能满足条件,则首先要找到人数超过上限的各组中共有多少人需要调走,用a进行累计;再找到人数不足下限的各组中所缺少的人数共需要多少人来补,用b进行累计。那么,最优的办法当然是让a去补b,因此a和b谁更大,谁就是需要的最少次数。

            (2)源程序。

    #include <stdio.h>

    #include <string.h>

    int main()

    {

        int num[51],n,i,sum,a,b,l,r;

        scanf("%d",&n);

        for (sum=0,i=0;i<n;i++)

        {

            scanf("%d",&num[i]);

            sum+=num[i];

        }

        scanf("%d%d",&l,&r);

        if (sum<n*l || sum>n*r) // 总人数不足或超过

           printf("-1 ");

        else

        {

            a=0;  b=0;

            for (i=0;i<n;i++)

            {

                if (num[i]>r) a+=num[i]-r;

                if (num[i]<l) b+=l-num[i];

            }

            printf("%d ",a>b?a:b);

        }

        return 0;

    }

    30-3 宅在家中看电视

    问题描述

    假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排,看尽量多的完整节目吗?

    输入格式

    输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。

    输出格式

    对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。

    输入样例

    12

    1 3

    3 4

    0 7

    3 8

    15 19

    15 20

    10 15

    8 18

    6 12

    5 10

    4 14

    2 9

    0

    输出样例

    5

            (1)编程思路。

            定义一个结构体

          struct showtime

          {

               int begin;

               int end;

          };

            用于保存电视节目的开始时间和结束时间。定义结构体数组show[101]保存输入的电视节目情况。

            采用贪心法求解。将电视节目(即结构体数组show)按结束时间从小到大排列(若结束时间相同,则按开始时间从大到小)。

    先设lastend=show[0].end,因为第1个元素的结束时间一定是最早的,然后从左到右遍历数组各元素,若当前元素的开始时间大于lastend,则可以看一个完整节目,计数,同时修改lastend使之等于当前元素的结束时间。直到数组全部元素遍历完。

            (2)源程序。

    #include <stdio.h>

    #include <algorithm>

    using namespace std;

    struct showtime

    {

        int begin;

        int end;

    };

    bool cmp(showtime a ,showtime b)

    {

        if(a.end != b.end)   

            return a.end < b.end;

        else

            return a.begin > b.begin;

    }

    int main()

    {

        showtime show[101];

        int n,i,cnt,lastend;

        while (scanf("%d",&n) && n!=0)

        {

            for (i = 0;i<n;i++)

            {

                scanf("%d%d",&show[i].begin,&show[i].end);

            }

            sort(show,show+n,cmp);    

            cnt = 1;

            lastend = show[0].end;

            for (i = 0;i < n ;i++)

            {

                if(lastend <= show[i].begin)

                {

                    cnt++;

                    lastend = show[i].end;

                }

            }

            printf("%d ",cnt);

        }

        return 0;

  • 相关阅读:
    线段树
    哈希,hash
    单调栈
    树的重心
    背包问题
    最小生成树
    二分图匹配
    题解 P6355 [COCI2007-2008#3] DEJAVU
    题解 P6745 『MdOI R3』Number
    题解 P2080 增进感情
  • 原文地址:https://www.cnblogs.com/cs-whut/p/12400370.html
Copyright © 2020-2023  润新知