目录
贪心法1题目总结
贪心法定义
通过一个简单策略,来求得问题的最优解
贪心法技巧
- 区间类的贪心,则可考虑左端点或右端点排序
贪心习题(选自题单)
#10080. 删数问题
思路
循环(n)次,每次遍历一遍字符串,如一字符比后面字符大,则字符全部往前移一位(达到删除作用)
注意
- 前导0的判断
代码
#include<bits/stdc++.h>
using namespace std;
string st;
int n,len,p;
bool flag;
int main(){
cin>>st>>n;
len=st.size();
while(n--){
flag=false;
for(int i=0;i<len-1;i++){
if(st[i]>st[i+1]){
for(int j=i;j<len;j++)
st[j]=st[j+1];
len--;
flag=true;
break;
}
}
if(!flag)len--;
}
while(p<len-1&&st[p]=='0')p++;
for(int i=p;i<len;i++)cout<<st[i];
return 0;
}
#10081. 活动选择
思路
首先,看到区间贪心,就想到排序!排完序,根据右端点,判断是否在一个区间的左端点内,如在则不选,反之就选
代码
#include<bits/stdc++.h>
using namespace std;
struct game{
int begin;
int end;
}a[1010];
int n,k,c;
bool cmp(game a,game b){
return a.end<b.end;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].begin>>a[i].end;
}
sort(a,a+n,cmp);
int end=a[0].end;
c=1;
for(int i=1;i<n;i++){
if(a[i].begin>=end){
c++;
end=a[i].end;
}
}
cout<<c;
return 0;
}
#10038. 最大整数
思路
这道题方法很简单,就是排序,可是怎么排序?这里有一个误区,会误以为直接将字符串按字典序从大到小排序就能AC,但是会有特例(如下图)。所以应根据连接起来后的字符串的字典序排序。
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
string s[30];
bool cmp(string a,string b){
if(a+b>b+a)return true;
else return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>s[i];
}
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++){
cout<<s[i];
}
return 0;
}
#10082. 整数区间
思路
恒古不变:看到区间想排序!右端点从小到大排序,判断是否在一个区间的范围内,是则答案数
+1
,反之继续找。
另外,选在区间的端点上比选在区间内的一个点上的方案要更优,因为选在端点上方便判断是否重叠。
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,ans,tmp;
struct node{
int x,y;
}a[10010];
bool cmp(node a,node b){
return a.y<b.y;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a+1,a+n+1,cmp);
tmp=INT_MIN;
for(int i=1;i<=n;i++){
if(a[i].x<=tmp){
continue;
}else{
ans++;
tmp=a[i].y;
}
}
printf("%d",ans);
return 0;
}
#10045. 零件分组
思路
恒古不变:看到区间想排序。按照长度从小到大排序,因为本题要求两个变量同时不下降排列,则先让其中一个变量不下降排列,则只要看另一个变量划分组别即可。
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
struct node{
int l,w;
}a[1010];
int ans;
bool f[1010];
bool cmp(node a,node b){
return a.l<b.l||a.l==b.l&&a.w<b.w;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].l,&a[i].w);
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
if(!f[i]){
int cur=a[i].w;
for(int j=i+1;j<=n;j++){
if(!f[j]){
if(cur<=a[j].w){
cur=a[j].w;
f[j]=true;
}
}
}
f[i]=true;
ans++;
}
}
printf("%d",ans);
return 0;
}
#10040. 纪念品组合
思路
将所有纪念品价格从小到大排序,一头一尾地选择符合要求的两个数(尽可能分更多的组),最后输出组数
注意
在算组数时,不要忘记算进单独一个数的情况
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int w,n,a[30010],ans,l,r;
bool f[30010];
int main(){
scanf("%d%d",&w,&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
// for(int i=1;i<=n;i++){
// if(!f[i]){
// for(int j=n;j>=i+1;j--){
// if(a[j]+a[i]<=w&&!f[j]){
// f[i]=f[j]=true;
// break;
// }
// }
// ans++;
// }
// }
l=1;
r=n;
while(l<r){
if(a[l]+a[r]<=w){
ans++;
l++;
r--;
}else{
r--;
ans++;
}
}
if(l==r)ans++;
printf("%d",ans);
return 0;
}
#10021. 均分纸牌
思路
计算出所有数的平均值,后根据平均值多到少分配(可以先“赊”一下,之后再还)
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[110],ave,ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ave+=a[i];
}
ave/=n;
for(int i=1;i<=n;i++){
if(a[i]==ave){
continue;
}
ans++;
if(a[i]>ave){
a[i+1]+=a[i]-ave;
}else{
a[i+1]-=(ave-a[i]);
}
a[i]=ave;
}
printf("%d",ans);
return 0;
}
#10043. 美元汇率
思路
每过去一天,计算一次若换算则能得到多少钱,不断找最多的,最后输出
代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double d=100,m;
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
double x;
cin>>x;
x/=100;
double dd=d;
d=max(d,m/x);
m=max(m,dd*x);
}
printf("%.2lf",d);
// cout<<fixed<<setprecision(2)<<d;
return 0;
}