20201206贪心法1课后总结

文章目录

    • 贪心法1题目总结
      • 贪心法定义
      • 贪心法技巧
      • 贪心习题([选自题单](http://wikioi.cn/training/mission/10))
        • [#10080. 删数问题](http://wikioi.cn/problem/10080)
          • 思路
          • 注意
          • 代码
        • [#10081. 活动选择](http://wikioi.cn/problem/10081)
          • 思路
          • 代码
        • [#10038. 最大整数](http://wikioi.cn/problem/10038)
          • 思路
          • 代码
        • [#10082. 整数区间](http://wikioi.cn/problem/10082)
          • 思路
          • 代码
        • [#10045. 零件分组](http://wikioi.cn/problem/10045)
          • 思路
          • 代码
        • [#10040. 纪念品组合](http://wikioi.cn/problem/10040)
          • 思路
          • 注意
          • 代码
        • [#10021. 均分纸牌](http://wikioi.cn/problem/10021)
          • 思路
          • 代码
        • [#10043. 美元汇率](http://wikioi.cn/problem/10043)
          • 思路
          • 代码

贪心法1题目总结

贪心法定义

通过一个简单策略,来求得问题的最优解

贪心法技巧

  1. 区间类的贪心,则可考虑左端点或右端点排序

贪心习题(选自题单)

#10080. 删数问题

思路

循环 n n n次,每次遍历一遍字符串,如一字符比后面字符大,则字符全部往前移一位(达到删除作用)

注意
  1. 前导0的判断
代码
#include
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
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,但是会有特例(如下图)。所以应根据连接起来后的字符串的字典序排序。

20201206贪心法1课后总结_第1张图片

代码
/*
ID: zhangbe5
TASK: test
LANG: C++
*/
#include
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
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
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
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
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
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<
	return 0;
}

你可能感兴趣的:(C++,#wikioi,算法,字符串,贪心算法,c++)