文章目录
贪心算法及练习题
简介
:
贪心算法(英语:greedy algorithm),是用计算机来模拟一个“贪心”的人做出决策的过程。这个人十分贪婪,每一步行动总是按某种指标选取最优的操作。而且他目光短浅,总是只看眼前,并不考虑以后可能造成的影响。
可想而知,并不是所有的时候贪心法都能获得最优解,所以一般使用贪心法的时候,都要确保自己能证明其正确性。
适用范围
:
贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。
1. 爱与愁的心痛
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 3e3 + 5;
int n,m;
int a[N];
int main(){
cin>>n>>m;
a[0]=0;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]=a[i]+a[i-1];
}
int ans=1000000;
for(int i=m;i<=n;i++){
int j=i-m;
ans=min(ans,a[i]-a[j]);
}
cout<<ans;
return 0;
}
模拟
贪心
枚举,暴力
2. 凌乱的yyy / 线段覆盖
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
#define pii pair<int, int>
int n;
pii a[N];
bool cmp(pair<int, int>a, pair<int, int>b)
{
if(a.second==b.second)
return a.first < b.first;
return a.second < b.second;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].first>>a[i].second;
}
/* 对结束时间进行从小到大排序 */
sort(a,a+n,cmp);
int b=a[0].second;
int ans=1;
for(int i=1;i<n;i++){
if(a[i].first>=b){
b=a[i].second;
ans++;
}else{
continue;
}
}
cout<<ans;
return 0;
}
搜索
贪心
排序
3. [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int maxn = 10005;
int a[maxn];
int main(){
int n;
priority_queue<int,vector<int>, greater<int> >pq;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&a[i]);
pq.push(a[i]);
}
int sum = 0;
while(true){
int x = pq.top();
pq.pop();
int y = pq.top();
pq.pop();
int z = x + y;
sum = sum + z;
if(pq.empty())
break;
pq.push(z); /* 将z值导入pq中 */
}
printf("%d\n",sum);
return 0;
}
贪心
二叉堆
优先队列
4. [NOIP2010 普及组] 接水问题
#include<iostream>
using namespace std;
const int maxn = 11000;
int a[maxn],ans;
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
int t=m+1; /* 下一个人的编号 */
while(t<=n+m){ /* 遍历完n个人的接水时间 */
for(int i=1;i<=m;i++){
a[i]--;
if(!a[i])
a[i]=a[t++];
}
ans++;
}
cout<<ans;
return 0;
}
模拟
搜索
贪心
5. [THUPC2017] 玩游戏
#include <iostream>
#include <cmath>
using namespace std;
#define int long long
signed main(){
int a,b;
cin>>a>>b;
int n=sqrt(2*(a+b)); /* 开方结果介于n到n+1之间 */
if(a+b!=n*(n+1)/2){
cout<<"No";
return 0;
}
cout<<n<<" ";
for(int i=n;i>0;i--){
if(i<a){
cout<<i<<" ";
a-=i; /* 结果不唯一 */
}else if(i==a){
cout<<i;
return 0;
}
}
return 0;
}
贪心
构造
6. 考验
#include <cstdio>
#include <map>
using namespace std;
#define int long long
int n,x;
map<int, int> m;
signed main(){
int t=0,ans=0;
scanf("%d", &n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
m[x]++;
if(m[x]==2)
t++;
if(t==2){
ans++;
t=0;
m.clear();
}
if(m[x]==4){
ans++;
t=0;
m.clear();
}
}
printf("%d",ans);
return 0;
}
动态规划,dp
贪心
7. [JOI 2020 Final] JJOOII 2
- 易得选出一个最短的子串包含
JJ... JOO... OII... I
为子序列,这个子串长度减去 (3K) 即为答案- 于是我们只需求出对于每个 (i),以 (i) 开头的后缀中该子序列最后一个字符所在的下标
- 预处理 (i) 之后字符
J
、O
、I
分别第 (K) 次出现的位置即可- (O(N))
#include <iostream>
using namespace std;
template <class T>
inline T Min(const T &a, const T &b) {
return a < b ? a : b;
}
const int N = 2e5 + 5, INF = 0x3f3f3f3f;
int n, k, nxt[N][3], top[3], stk[3][N], ans = INF;
char s[N];
int main()
{
cin >> n >> k;
scanf("%s", s + 1); /* 从s[1]开始填充字符串 */
for (int i = n; i >= 1; i--)
{
int c = s[i] == 'I' ? 2 : (s[i] == 'O' ? 1 : 0);
/* 将J用0代替,O用1代替,I用2代替 */
stk[c][++top[c]] = i;
for (int c = 0; c < 3; c++)
if (top[c] >= k) nxt[i][c] = stk[c][top[c] - k + 1] + 1;
/* 相当于一个索引,保证每次索引结束后的包含k个J或者k个O或者k个I */
/* 最后索引完的值为包含k次的J,O,I的一个最短子序列 */
}
for (int i = 1; i <= n; i++)
{
int lst = nxt[nxt[nxt[i][0]][1]][2];
if (lst) ans = Min(ans, lst - i - k * 3);
}
return cout << (ans == INF ? -1 : ans) << endl, 0;
}
贪心