例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;
}