1. 程式人生 > 實用技巧 >20201206貪心法1總結

20201206貪心法1總結

目錄

貪心法1題目總結

貪心法定義

通過一個簡單策略,來求得問題的最優解

貪心法技巧

  1. 區間類的貪心,則可考慮左端點或右端點排序

貪心習題(選自題單

#10080. 刪數問題

思路

迴圈\(n\)次,每次遍歷一遍字串,如一字元比後面字元大,則字元全部往前移一位(達到刪除作用)

注意
  1. 前導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;
}