贪心算法合集(备赛中)

67 篇文章 0 订阅
15 篇文章 0 订阅

A.活动安排

 

 思路:

互不兼容的区间,就将右区间排序,然后取没用交集的区间即可,比较简单。

#include<bits/stdc++.h>
using namespace std;
struct node
{
	 int l,r;

}n[1010];
/*class Cmp
{
	pubilc:
		   bool operator()(const node &a,const node &b)
		   {
			  ;
		   }
}*/
bool cmp1(node a,node b)
{
	return a.r<b.r;
}
int main()
{
	    ios::sync_with_stdio(false);
	    cin.tie(NULL);
	    cout.tie(NULL);
	    int num;cin>>num;
	    for(int i=1;i<=num;i++)cin>>n[i].l>>n[i].r;
	    sort(n,n+num,cmp1);
	    int maxn=0;
        int index=0;
	    for(int i=1;i<=num;i++)
	    {
		   if(index<=n[i].l)
           {
               maxn++;
               index=n[i].r;
           }
		}
		cout<<maxn;

}

B.种树

 思路:区间的覆盖问题,采取一个巧妙的贪心策略。对于两个互交区间,我们可以先在前一个区间中,将满足区间的树的数目加进去,同时我们要在加树的同时将区间覆盖,我们必须先从右区间向左区间去遍历,在遍历的同时我们要顺便做标记,用于我们之后判断相交区间,这样就能保证最优覆盖,因为还是区间覆盖问题的区间相交问题,我们还是将区间的右边界作为关键值用来排序。所以根据以上思路,我们的关键就是要定义一个time数组,用来判断区间相交问题。具体代码如下。

​
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int maxh=3e5+10;
int num1,num2,mum;
int m[maxh];
struct node
{
	 int l,r,v;
}n[maxn];
bool cmp1(node a,node b)
{
	return a.r<b.r;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin>>num1>>num2;
	for(int i=1;i<=num2;i++)cin>>n[i].l>>n[i].r>>n[i].v;
	sort(n+1,n+num2+1,cmp1);
	for(int i=1;i<=num2;i++)
	{
	   int sum=0;
	   for(int i1=n[i].l;i1<=n[i].r;i1++)//这个就是判断覆盖区间优先在覆盖区间中加树是否加过了
	   {
		  if(m[i1])sum++;
	   }
	   if(sum>=n[i].v)continue;//如果这个满足情况了,说明在循环上一个已经满足要求了
	   for(int i1=n[i].r;i1>=n[i].l;i1--)//不满足情况的情况下,从右边界向左边界遍历加树,顺便标记。
	   {
		  if(!m[i1])
		  {
		  	   m[i1]=1;
		  	   sum++;
			   mum++;
			   if(sum==n[i].v)break;//加到需求值离开即可,万不可以在继续加树。
		  }
	   }
	}
	cout<<mum;
}

​

C.喷水装置

 

 

思路:先将喷水半径对应的园与其所能覆盖的最大长方形做比较得出能覆盖的最大左右边界,然后就会转化为区间覆盖的全覆盖问题,这样我们的贪心策略就明确了,关于区间覆盖的全覆盖问题,我们要将左边界进行排序,我们从0开始寻找最长的覆盖区间,然后0就要变成最长覆盖区间的右边界,然后我们重复这个过程,即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct node
{
	double l,r;
}n[maxn];
bool cmp(node a,node b)
{
	return a.l<b.l;
}
void solve()
{
	int a;
    double c,k;
   cin>>a>>c>>k;
   for(int i=1;i<=a;i++)
   {
   	double x,y;cin>>x>>y;
   	double len=y*y-(k/2.00)*(k/2.00);//转化为左右区间覆盖的值
   	if(len<=0)//如果圆太小了就没有覆盖权力了
   	{
	 n[i].l=0;
	 n[i].r=0;
	}
	else if(len>0)//赋值左右区间
	{
	  n[i].l=x-sqrt(len);
	  n[i].r=x+sqrt(len);
	}
   }
   sort(n+1,n+1+a,cmp);
   double sum=0.0;
   int count=0;
   while(sum<c)//知道全覆盖了
   {
   	double maxn=0.0;//保留覆盖的最大值
   	for(int i=1;i<=a&&n[i].l<=sum;i++)
   	{
	   if(n[i].r-sum>maxn)
	   {
		maxn=n[i].r-sum;
	   }
	}
	if(maxn==0)break;//找不到了就没有蓬头可以覆盖了直接走。
	else
	{
	count++;//蓬头数目增加
	sum+=maxn;//覆盖最大值进行赋值
	}
   }
   if(sum<c)cout<<-1<<endl;
   else cout<<count<<endl;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int num;cin>>num;
	while(num--)
	{
	 solve();
	}
}

D.加工生产调度

 思路:这道题我们要取最棒的加工顺序,首先我们要知道,要先进行了a组操作才可以进行b组操作,所以我们要将a<b的排到前面a>b的排到后面a==b的这种情况不会做任何贡献,贪心策略就是因为a是小于b的排前面,这样会尽可能缩短空闲时间,并且a<b的加工情况我们还需要将a小的排前面,之后我们对于a>b的我们同样要将空闲时间最小化,我们将a>b的情况放在后面并且按b的递减排序,这样就能保证空闲时间最小化。之后我们就开始按这个顺序进行加工了。在加工过程中,我们要确定下我们在a车间的加工时间和在b车间的加工时间,这样我们就可以选取目前车间的结束时间。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node
{
	int a,b;
	int flag;
	int index;
}N[maxn];
int num;
bool cmp(node x,node y)
{
	if(x.flag==y.flag)//具体就是将a<b的排前面,a>b的排后面
	{
		 if(x.flag==1)
		 {
		   return x.b>y.b;
		 }
		 else
		 {
		 	return x.a<y.a;
		 }
	}
	return x.flag<y.flag;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
	cin>>num;
	for(int i=1;i<=num;i++)cin>>N[i].a;
	for(int i=1;i<=num;i++)cin>>N[i].b;
	for(int i=1;i<=num;i++)
	{
	   if(N[i].a<N[i].b)N[i].flag=0;
	   else if(N[i].a>N[i].b)N[i].flag=1;
	   N[i].index=i;
	}
	sort(N+1,N+1+num,cmp);
	int maxa=0;
	int maxb=0;
	for(int i=1;i<=num;i++)
	{
		   maxa+=N[i].a;//a车间的累计时间
		   maxb=max(maxb,maxa)+N[i].b;//b车间的累积时间
	}
	cout<<maxb<<endl;
	cout<<N[1].index;
	for(int i=2;i<=num;i++)cout<<" "<<N[i].index;
}

E.智力大冲浪

 

 思路:对于取最优的情况我们当然要将money数进行从大到小排序喽,但关键的是我们要在时间区间内添数,如果添数失败了,就说明这种情况不得不舍去了。

 #include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e4+10;
struct node
{
	int time;
	int money;
}N[maxn];
int M[maxn]={0};
int num1,num2;
bool cmp(node a,node b)
{
	return a.money>b.money;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>num1>>num2;
	for(int i=1;i<=num2;i++)cin>>N[i].time;
	for(int i=1;i<=num2;i++)cin>>N[i].money;
	sort(N+1,N+1+num2,cmp);
	int cost=0;
	for(int i=1;i<=num2;i++)
	{
		   int sign=0;
		   for(int i1=N[i].time;i1>=1;i1--)//关键在区间中添数,添数成功后进行标记,添数失败后进行抛弃
		   {
		   	    if(M[i1]==0)
		   	      {
					  M[i1]=1;
					  sign=1;
					  break;
				  }
		   }
		  if(sign==0)
		  {
			cost+=N[i].money;
		  }
	}
	cout<<num1-cost;
}

F.数列极差

 思路:建立一个最大堆和一个最小堆依次进行题目步骤即可,唯一要注意的是边界问题,在堆只剩下一个元素时要结束,并进行结果的输出。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    priority_queue<int,vector<int>,greater<int>>q1;
    priority_queue<int>q2;
    int num;
    cin>>num;
    int num1;
    for(int i=1;i<=num;i++)
    {
        cin>>num1;
        q1.push(num1);
        q2.push(num1);
    }
    cin>>num1;
    int mas;
    while(q2.size()>1)
    {
        int x=q2.top();
        q2.pop();
        int y=q2.top();
        q2.pop();
        int z=x*y+1;
        q2.push(z);
    }
    mas=q2.top();
    int mis;
    while(q1.size()>1)
    {
        int x=q1.top();
        q1.pop();
        int y=q1.top();
        q1.pop();
        int z=x*y+1;
        q1.push(z);
    }
    mis=q1.top();
    cout<<mis-mas;
}

G.数列分段

 思路:贪心策略从最小的开始遍历,如果一直累加知道出现大于M的情况,划分段数加一,并且累加值变为目前的边界值,反复执行即可。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn=1e6+10;
int N[maxn];
int num1,num2;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>num1>>num2;
    for(int i=1;i<=num1;i++)cin>>N[i];
    int sum=0;
    int k=0;
    for(int i=1;i<=num1;i++)
    {
       k+=N[i];
       if(k>num2)
       {
           sum++;
           k=N[i];
       }
    }
    if(k<=num2)sum++;
    cout<<sum;
}

I:家庭作业

 

思路:介个题就跟我们那个取钱的那个题一样了,就是把最多的钱数排到前面,然后然后往时间区间里面填数即可,但这里需要一个小优化。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e7+10;
struct node
{
    int time;
    int grad;
}N[maxn];
int times[maxn]={0};
bool cmp(node a,node b)
{
    return a.grad>b.grad;
}
int num;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>num;
    for(int i=1;i<=num;i++)cin>>N[i].time>>N[i].grad;
    sort(N+1,N+1+num,cmp);
    int sum=0;
    int stop=0;//与那个题不同的时,这里我们要加一个停止时间,如果时间区间比这个小就无法填数,只能抛弃当前的钱数了。
    for(int i=1;i<=num;i++)
    {
        if(stop>=N[i].time)continue;
        int flag=1;
        for(int i1=N[i].time;i1>=1;i1--)
        {
            if(times[i1]==0)
            {
                times[i1]=1;
                flag=0;
                sum+=N[i].grad;
                break;
            }
        }
        if(flag==1)stop=N[i].time;
    }
    cout<<sum;
}

 J.钓鱼

 

 思路:我们枚举下,一个湖,两个湖,....N个湖的情况。假设我们要进行N个湖的情况,并且我们要去最远的湖,并且我们还可能回来,这个时候我们的最优贪心策略就是直接到最远的湖,这样之后我们直接开始每一个湖的最优钓鱼数选取即可。这里这个直接到最远的湖,就是代表着我们走过的最优路径,同时这也代表着我们在N个湖当中可以瞬间移动,而这个瞬间移动的这个最优贪心时间路程就是我们一开始从第一个湖直接到最远湖的时间路程。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+10;
typedef pair<int,int>pi;
int maxs(int a,int b)
{
    if(a>b)return a;
    else return b;
}
int num1,times;
int N[maxn],M[maxn],O[maxn];
signed main()
{
    cin>>num1;
    cin>>times;
    for(int i=1;i<=num1;i++)cin>>N[i];
    for(int i=1;i<=num1;i++)cin>>M[i];
    for(int i=2;i<=num1;i++)cin>>O[i];
    int sum=0;
    for(int i=1;i<=num1;i++)
    {
        int h=times*60;
        int ans=0;
        priority_queue<pi,vector<pi>>que;
        for(int i1=1;i1<=i;i1++)//我们的最优贪心时间路程
        {
            que.push({N[i1],i1});
            h-=5*O[i1];
        }
        while(h>0&&!que.empty())//之后相当于可以瞬间移动了,我们瞬间选取最优的🐟数目即可。
        {
            pi t=que.top();
            que.pop();
            ans+=t.first;
            h-=5;
            t.first-=M[t.second];//选完之后要递减一下
            if(t.first<=0)t.second=0;//减完之后就赋值0
            else//如果鱼塘中还有的话就继续放到堆中
            {
                que.push({t.first,t.second});
            }
        }
        sum=maxs(ans,sum);//选取所有枚举情况的最优值。
    }
    cout<<sum;
}

K.糖果传递

 思路:这道题选取的贪心策略要用数学公式进行推导一下,具体过程如下。

 

​
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e7+10;
int num;
int N[maxn];
int M[maxn];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>num;
    int sum=0;
    for(int i=1;i<=num;i++)
    {
        cin>>N[i];
        sum+=N[i];
    }
    int ave=sum/num;
   // M[1]=ave-N[1];//这个好像写进去没啥事
    for(int i=1;i<=num;i++)//c数组求法
    {
        M[i]=M[i-1]-N[i]+ave;
    }
    sort(M+1,M+1+num);//排序取中间就是最小代价
    int ans=0;
    for(int i=1;i<=num;i++)
    {
        ans+=abs(M[1+num>>1]-M[i]);
    }
    cout<<ans;
}

​

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

B程洪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值