贪心例题
1:数列极限
https://ac.nowcoder.com/acm/problem/50166
思路:
考虑3个数a,b,c. a < b < c,有三种取法:
m1 = (ab+1)c = abc + c
m2 = (ac+1)b = abc + b
m3 = (bc+1)a = abc + a
m1 > m2 > m3
可以看出来要最后值最大的话优先取两个小的数,值最小的话优先取两个最大的数。
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #define LL long long #define inf 0x3f3f3f3f const int mod = 1e9+7; using namespace std; int main() { int n; priority_queue<int,vector<int>,less<int> >q1; priority_queue<int,vector<int>,greater<int> >q2; cin >> n; for(int i = 0; i < n; i++){ int x; cin >> x; //sl.push_back(x); q1.push(x); q2.push(x); } cin >> n; int mmin,mmax; while(q1.size() != 1){ int a = q1.top(); q1.pop(); int b = q1.top(); q1.pop(); q1.push(a*b+1); } mmax = q1.top(); while(q2.size() != 1){ int a = q2.top(); q2.pop(); int b = q2.top(); q2.pop(); q2.push(a*b+1); } mmin = q2.top(); int m = mmin - mmax; cout << m; return 0; }
2:均分纸牌
思路:
1:这类问题一般是让所有的数到平均数次数最少。
2:我们要移动的次数最少,那么对于每堆牌,我们考虑最多移动一次,因为移动两次或两次以上是一种浪费。采用“移动一次使得一堆牌数达到平均值”的贪心策略:先把每堆的牌数减去平均数,然后由左而右的顺序移动纸牌。若第i堆纸牌的张数a[i]不为0,则将值移动到下一堆。
3:求出最后的a[i]后,我们可以发现a[i]<0表示需要到右边堆去拿a[i]张牌,那么第i+1堆离平均数为a[i+1] += a[i],a[i]>0那么我们就拿a[i]张牌给第i+1堆,那么第i+1堆离平均数为a[i+1] += a[i]。
https://ac.nowcoder.com/acm/problem/16739
#include <bits/stdc++.h> #include <iostream> #include <cstdio> #define LL long long #define inf 0x3f3f3f3f const int mod = 1e9+7; using namespace std; int main() { int a[105],n,sum; cin >> n; sum = 0; for(int i = 1; i <= n; i++) cin >> a[i],sum += a[i]; int ave = sum / n; for(int i = 1; i <= n; i++) a[i] -= ave; int ans = 0; for(int i = 1; i <= n; i++){ if(a[i] != 0) a[i+1] += a[i],ans++; // if(a[i] != 0){ // //a[i+1] += a[i]; // if(a[i] < 0) cout << i+1 << "-->" << i << ":" << abs(a[i]) << endl; // else cout << i << "-->" << i+1 << ":" << a[i] << endl; // } } cout << ans; return 0;
3:接水问题
思路:从每一组接水的人中选出接水时间最少的那个人,让下一个人接替他的位置接水并加上他的接水时间,这样依次算出每个水龙头的接水总时间,最后选最长节水时间即可。
#include <iostream> #include <cstdio> #include <algorithm> #include <stack> #include <queue> #include <string.h> #include <cmath> #include <bitset> #define ll long long const int inf = 0x3f3f3f3f; const int mod = 1e9+7; using namespace std; //void print(char a[]) //{ // printf("%d %c\n",strlen(a),a[2]); //} int main() { int n,m,a[100005],b[105]; cin >> n >> m; int ans = 0,mmax = 0; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++){ int k = 1; for(int j = 1; j <= m; j++){ if(b[k] > b[j]){ k = j; } } b[k] += a[i]; } for(int i = 1; i <= m; i++) { mmax = max(mmax,b[i]); } cout << mmax; return 0; }
4:删数问题
贪心策略:优先从最高位查找,如果从前往后是递增的,则删除递增序列的最后一个,如果递减就删除递减序列的第一个。
一段string的操作代码:
int main() { string s; cin >> s; s.erase(s.begin()+0); s.insert(s.begin()+0,'2'); cout << s; return 0; }
#include <iostream> #include <cstdio> #include <algorithm> #include <stack> #include <queue> #include <string.h> #include <cmath> #include <bitset> #define ll long long const int inf = 0x3f3f3f3f; const int mod = 1e9+7; using namespace std; int main() { string s; int m,n; while(cin >> s){ scanf("%d",&m); for(int i = 1; i <= m; i++){ int n = s.length(); for(int i = 0; i < n; i++){ if(i == n - 1){ s.erase(s.begin()+i); break; } if(s[i] > s[i + 1]){ s.erase(s.begin()+i); break; } } } int flag = 0; n = s.length(); for(int i = 0; i < n; i++){ if(s[i] == '0' && flag == 0) continue; else{ cout << s[i]; flag = 1; } } if(flag == 0) cout << '0'; cout << "\n"; } return 0; }
4:货仓选址
给定数轴上的n个点,在数轴上的所有点中,中位数离所有顶点的距离之和最小。凡是能转化为这个模型的题目都可以用中位数求解。
#include <iostream> #include <cstdio> #include <algorithm> #include <stack> #include <queue> #include <string.h> #include <cmath> #include <bitset> #include <set> #include <map> #define ll long long const int inf = 0x3f3f3f3f; const int mod = 1e9+7; const ll ds = 1e15+7; const double p = 3.141592653589793238462643383; using namespace std; int a[100005]; int main() { int n,x,m,t; cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; sort(a+1,a+n+1); int ans = 0; int d; if(n & 1) d = (a[n/2+1] ) / 2; else d = (a[n/2] + a[n/2+1]) / 2; for(int i = 1; i <= n; i++){ ans += abs(a[i] - d); } cout << ans; return 0; }