【洛谷算法1-5】贪心

贪心我觉的最难的在于证明,如何这样最优。

P1223 排队接水 【贪心 / 前缀和】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1223

思路: 将其从小到大排序,这样的话平均时间最小,求其前缀和数组。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct student
{
	int id;
	int t;
}stu[1005];
bool cmp(student a,student b)
{
	return a.t<b.t;
}
long long int sum;
long long int s[1005];
int main(void)
{
	int n; cin>>n;
	for(int i=1;i<=n;i++) cin>>stu[i].t,stu[i].id=i;
	sort(stu+1,stu+n+1,cmp);//从小到大排序
	for(int i=1;i<=n;i++) s[i]=s[i-1]+stu[i].t;//前缀和数组
	for(int i=1;i<=n;i++) cout<<stu[i].id<<" ",sum+=s[i-1];
	printf("\n%.2lf\n",sum*1.0/n);
	return 0;
}

P1803 凌乱的yyy / 线段覆盖 【贪心 / 区间合并】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1803

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
vector<PII> ve;
int main(void)
{
	int n; cin>>n;
	int a,b;
	for(int i=0;i<n;i++) cin>>a>>b,ve.push_back({a,b});
	sort(ve.begin(),ve.end());
	int ans=1,endx=ve[0].y;
	for(int i=1;i<ve.size();i++)
	{
		if(endx<=ve[i].x) ans++,endx=ve[i].y;//情况二
		if(ve[i].y<endx) endx=ve[i].y;//情况一
	}
	if(n==0) cout<<0<<endl;
	else cout<<ans<<endl;
}

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G 【贪心 / 堆 / 优先队列】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1090
方法: 优先队列,它的底层就是堆,故可以直接用,而不用手写堆了。
先排列,取最小的数,再将和入队。

#include<cstdio>
#include<iostream>
#include<algorithm> 
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int> >a;//队首的数小
int sum;
int temp;
int main(void)
{
	int n; cin>>n;
	int number;
	for(int i=0;i<n;i++) cin>>number,a.push(number);
	while(a.size()>1)//合并成一个堆了 
	{
		temp+=a.top(),a.pop();
		temp+=a.top(),a.pop();
		a.push(temp);
		sum+=temp;
		temp=0;	
	}
	cout<<sum<<endl; 
	return 0;
} 

看题解区大佬的做法:

#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	scanf("%d",&n);
	int a[n+5];
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	int ans=0;
	for(int i=1;i<=n-1;i++)
	{
		ans+=(a[i]+a[i+1]);
		a[i+1]+=a[i];//俩数合并
		for(int j=i+1;j<=n-1;j++)//将合并后的后面数据排序
		{
			if(a[j]>a[j+1])
				swap(a[j],a[j+1]);
			else
				break;
		}
	}
	printf("%d\n",ans);
	return 0;
}

P1208 [USACO1.3]混合牛奶 Mixing Milk 【典型的贪心】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1208

#include<cstdio> 
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
vector<PII> ve;
int ans;
int main(void)
{
	int sum,n; cin>>sum>>n;
	int a,b;
	for(int i=0;i<n;i++) cin>>a>>b,ve.push_back({a,b}); 
	sort(ve.begin(),ve.end());
	for(int i=0;i<n;i++)
	{
		if(sum==0) break;//买完了
		if(sum<ve[i].y) ans+=sum*ve[i].x,sum=0;//这一家可以买完了
		if(sum>=ve[i].y) ans+=ve[i].x*ve[i].y,sum-=ve[i].y;//买的还不足够
	}
	cout<<ans<<endl;
	return 0;
}

P1094 [NOIP2007 普及组] 纪念品分组 【贪心 / 双指针】

在这里插入图片描述
https://www.luogu.com.cn/problem/P1094

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[50005];
int ans;
int main(void)
{
	int sum; cin>>sum;
	int n;   cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	int l=0,r=n-1;
	while(l<r)
	{
		if(a[l]+a[r]<=sum) ans++,l++,r--;
		else r--,ans++;
	}
	if(l==r)cout<<ans+1<<endl; //最后一次只有一个人
	else cout<<ans<<endl;  //最后一组看好组完一队
	return 0;
}

P4995 跳跳!

在这里插入图片描述
https://www.luogu.com.cn/problem/P4995

要想最大: 一定是 一个最大的数,一个最小的数一次排列。 数据范围很小直接暴力模拟。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int a[305];
long long int sum=0;
bool cmp(int a,int b)
{
	return a>b;
}
int main(void) 
{
	int n; cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	sum+=pow(a[n-1],2);//先找打最大的数跳
	int temp=a[n-1];
	n--;
	while(1)
	{
		sort(a,a+n,cmp); //从大到小排 
		int w=a[n-1];//取最小的数
		sum+=pow(abs(temp-w),2);
		n--;//去掉跳过的数
		if(n==0) break;//都跳完了
		sort(a,a+n);
		temp=a[n-1];//取最大的数
		sum+=pow(abs(temp-w),2);
		n--;
		if(n==0) break;
	}
	cout<<sum<<endl;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值