20201206貪心法1總結
阿新 • • 發佈:2020-12-06
目錄
貪心法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;
}