目录
先言
今天讲贪心算法的第3~5例题
例题三:删数问题(NOI1994)
题目描述
【题目描述】
输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序组成一个新的正整数。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小。
输出新的正整数。(N不超过240位)输入数据均不需判错。
【输入】
n
s
【输出】
最后剩下的最小数。
【样例输入】
175438
4
【样例输出】
13
算法分析
由于正整数n的有效数位为240位,所以很自然地采用字符串类型存贮n。那么如何决定哪s位被删除呢?是不是最大的s个数字呢?显然不是,大家很容易举出一些反例。为了尽可能逼近目标,我们选取的贪心策略为:每一步总是选择一个使剩下的数最小的数字删去,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字;否则删除第一个递减区间的首字符,这样删一位便形成了一个新数字串。然后回到串首,按上述规则再删下一个数字。重复以上过程s次为止,剩下的数字串便是问题的解了。
例如:n=175438
s=4
删数的过程如下:
n=175438 //删掉7
15438 //删掉5
1438 //删掉4
138 //删掉8
13 //解为13
这样,删数问题就与如何寻找递减区间首字符这样一个简单的问题对应起来。不过还要注意一个细节性的问题,就是可能会出现字符串串首有若干0的情况,甚至整个字符串都是0的情况。按以上贪心策略编制的程序框架如下
输入n,s;
for (i=1;i<=s;++i) { //一共要删除s个字符
for ( j=0;j<len-1;++j ) //从串首开始找,len是n的长度
if ( n[j]>n[j+1] ) { //找到第一个符合条件的
for (k=j;k<len-1;++k) //删除字符串n的第j个字符,后面字符往前整
n[k]=n[k+1];
break;
}
--len; //长度减1
}
输出n; //删去串首可能产生的无用零
标准程序 - 字符串String
#include<string>
#include<iostream>
using namespace std;
int main() {
int s;
string n;
cin >> n >> s;
for (int i; i = 0, s--; n.erase(i, 1))
while (i < n.size() && n[i] <= n[i+1])
++i;
while (n.size() > 1 && n[0] == '0')
n.erase(0, 1);
cout << n << endl;
return 0;
}
例题四:拦截导弹问题
题目描述
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
【输入格式】
n颗依次飞来的高度(1≤n≤1000).
【输出格式】
要拦截所有导弹最小配备的系统数k。
【输入样例】missile.in
389 207 155 300 299 170 158 65
【输出样例】missile.out
2
【输入输出样例】
输入:导弹高度: 7 9 6 8 5
输出:导弹拦截系统K=2
输入:导弹高度: 4 3 2
输出:导弹拦截系统K=1
算法分析
按照题意,被一套系统拦截的所有导弹中,最后一枚导弹的高度最低。设:
k为当前配备的系统数;
l[k]为被第k套系统拦截的最后一枚导弹的高度,简称系统k的最低高度(1≤k≤n)。
我们首先设导弹1被系统1所拦截(k←1,l[k]←导弹1的高度)。然后依次分析导弹2,…,导弹n的高度。
若导弹i的高度高于所有系统的最低高度,则断定导弹i不能被这些系统所拦截,应增设一套系统来拦截导弹I(k←k+1,l[k]←导弹i的高度);若导弹i低于某些系统的最低高度,那么导弹i均可被这些系统所拦截。究竟选择哪个系统拦截可使得配备的系统数最少,我们不妨采用贪心策略,选择其中最低高度最小(即导弹i的高度与系统最低高度最接近)的一套系统p(l[p]=min{l[j]|l[j]>导弹i的高度};l[p]←导弹i的高度)(1≤j≤k)。这样可使得一套系统拦截的导弹数尽可能增多。
依次类推,直至分析了n枚导弹的高度为止。此时得出的k便为应配备的最少系统数。
主要框架(标准程序)
参考程序主要框架如下:
k=1; l[k]=导弹1的高度;
for (i=2; i<=n; ++i)
{
p=0;
for (j=1; j<=k; ++j)
if (l[j]>=导弹i的高度) { if (p==0) p=j;
else if (l[j]<l[p]) p=j;
} //贪心
if (p==0) { ++k; l[k]=导弹i的高度; } //增加一套新系统
else l[p]=导弹i的高度; //贪心,更新第p套系统的最低高度
}
输出应配备的最少系统数K。
例题五:活动选择
题目描述
学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。
现在给出n个活动使用礼堂的起始时间begini和结束时间endi(begini<endi),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。
【输入】 第一行一个整数n(n<=1000);
接下来的n行,每行两个整数,第一个begini,第二个是endi (begini<endi<=32767)
【输出】 输出最多能安排的活动个数。
【样例输入】
11
3 5
1 4
12 14
8 12
0 6
8 11
6 10
5 7
3 8
5 9
2 13
【样例输出】
4
算法分析
• 算法模型:给n个开区间(begini,endi), 选择尽量多的区间, 使得两两不交。
• 做法: 首先按照end1<=end2<…<=endn的顺序排序,依次考虑各个活动, 如果没有和已经选择的活动冲突, 就选; 否则就不选。
• 正确性: 如果不选end1, 假设第一个选择的是endi,则如果endi和end1不交叉则多选一个end1更划算; 如果交叉则把endi换成end1不影响后续选择。
标准程序
(省略前部)
int n,begin[1001],end[1001];
void init()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>begin[i]>>end[i];
}
void qsort(int x,int y)
{
int i,j,mid,t;
i=x; j=y; mid=end[(x+y)/2];
while(i<=j)
{
while(end[i]<mid) ++i;
while(end[j]>mid) --j;
if(i<=j)
{
t=end[j]; end[j]=end[i]; end[i]=t;
t=begin[j]; begin[j]=begin[i]; begin[i]=t;
++i;j;
}
}
if(x<j) qsort(x,j);
if(i<y) qsort(i,y);
}
void solve()
{
int ans=0;
for(int i=1,t=-1;i<=n;++i)//在初始化循环变量的同时,初始化t。
//令t=-1可以使第一个区间与其他区间的操作相同
if(begin[i]>=t)
{ //当前活动开始时间与之前选择的活动结束时间不冲突,就接受当前活动
++ans;t=end[i];
}
cout<<ans<<endl;
}
int main()
{
init();
qsort(1,n);
solve();
return 0;
}
标签:int,系统,编程,高度,C++,导弹,拦截,例题,贪心
From: https://blog.csdn.net/weixin_68261272/article/details/143090504