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]={'\0'};

    int s,i,j;

    scanf("%s",num);

    scanf("%d",&s);

    while (s>0)    // 循環s次,每次刪除一個數字

    {

           i=0;          // 每次刪除后從頭開始搜尋待刪除數字

          while (num[i]!='\0' && 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]=='\0') printf("0\n");

    else  printf("%s\n",&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]={'\0'},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]='\0';

    printf("%s\n",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]='\0';

    printf("%s\n",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\n");

    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\n",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\n",cnt);

    }

    return 0;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM