LeeCode 1705 吃苹果的最大数量(贪心+优先队列)

题目描述:

        题目链接:力扣

        有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,树上不会长出新的苹果,此时用 apples[i] == 0 且 days[i] == 0 表示。   

        你打算每天 最多 吃一个苹果来保证营养均衡。注意,你可以在这 n 天之后继续吃苹果。给你两个长度为 n 的整数数组 days 和 apples ,返回你可以吃掉的苹果的最大数目。

示例1:

        输入apples = [1,2,3,5,2], days = [3,2,1,4,2]
        输出7
        解释:你可以吃掉 7 个苹果:
                - 第一天,你吃掉第一天长出来的苹果。
                - 第二天,你吃掉一个第二天长出来的苹果。
                - 第三天,你吃掉一个第二天长出来的苹果。过了这一天,第三天长出来的苹果就已经腐烂了。
                - 第四天到第七天,你吃的都是第四天长出来的苹果。

题目分析:

        1.首先根据题目信息,可以在n天之后继续吃苹果,可以分为两种情况分别讨论,n天之前,n天之后。

        2.然后根据贪心思路,只要每次吃的都是腐烂日期最早的苹果那么一定能吃到最多的苹果,但是需要注意,腐烂日期一定要大于等于当前日期 i 

        3.根据前两点分析,我们需要维护一个优先队列queue存储第i天苹果的个数和腐烂日期(用一个数组表示int[] temp=new int[2], temp[0] 表示腐烂日期,temp[1]表示苹果数量),然后根据腐烂日期进行递增排序,进行一次for(int i=0;i<n;i++)循环,每次循环代表一天,每天需要将已经腐烂的苹果处理掉,也就是将腐烂日期小于 i 的元素出队;将当天生产的苹果放入优先队列,队首元素temp[1] -1 表示已经吃过一个苹果。

        4.n天之后已经不生产苹果了,所以每次进行队首元素判断,ans(代表吃掉的苹果数)+=Math.min(queue.peek()[0]-n+1,queue.peek()[1]);队首这一批苹果一定是腐烂日期最小的,此时有两种情况:

                1)队首元素苹果数量很多,每天吃一个,直到这一批苹果的腐烂日期也还没吃完,所以只能吃:(当前苹果腐烂日期)queue.peek()[0] - (上一批苹果吃完的日期)n+1;

                2)队首元素苹果数量很少,每天吃一个,没有到苹果腐烂日期就吃完了,所以只能吃:queue.peek()[1]

代码(JAVA版本)

public int eatenApples(int[] apples, int[] days) {
    
    //1.首先维护一个优先队列,队列元素是一维数组,根据腐烂日期排序
    PriorityQueue<int[]> queue=new PriorityQueue<>((a,b)->a[0]-b[0]);

    //2.for循环模拟每天吃苹果过程(n天前)
    int n=apples.length,ans=0;
    for(int i=0;i<n;i++){
        //2.1每天吃苹果之前先去除掉腐烂的苹果
        while(!queue.isEmpty()&&queue.peek()[0]<i)queue.poll();
        
        //2.2加入当天生产的苹果(如果当天有苹果)日期从0开始算
        if(apples[i]>0){
            queue.add(new int[]{i+days[i]-1,apples[i]});
        }
        //2.3吃苹果,注意一定要判断队列是否为空
        if(!queue.isEmpty()){
            //吃苹果
            queue.peek()[1]--;
            ans++;
            //如果苹果吃完,就要从队列中取出
            if(queue.peek()[1]==0)
                queue.poll();
        }
    }
    
    //3.n天之后,第一次循环开始时n等于apples.length,但是之后需要更新为上次吃苹果的日期
    while(!queue.isEmpty()){
        //3.1处理掉腐烂的苹果
        while(!queue.isEmpty()&&queue.peek()[0]<n){
            queue.poll();
        }
        //3.2处理腐烂时间最小的一批苹果,注意一定判断队列是否为空
        if(!queue.isEmpty()){
            int[] temp=queue.poll();
            //这批苹果数量
            int num=temp[1];   
            //这批苹果的腐烂日期
            int rotdays=temp[0];

            ans+=Math.min(num,temp[0]-n+1);
            n+=Math.min(num,temp[0]-n+1);
        }
    }
    
    return ans;    
}

Dijkstra算法是解决单源最短路径问题的一种经典算法,其基本思想是利用贪心的思想,每次选取未确定最短路径的节点中距离起点最近的节点,然后根据该节点更新与该节点相邻的节点的距离。具体实现可以采用邻接表或邻接矩阵来表示图,同时利用优先队列STL来维护节点距离的更新。 算法步骤如下: 1.初始化:将起点的距离设置为0,其余节点的距离设置为无穷大,将所有节点标记为未确定最短路径。 2.选择当前距离起点最近的未确定最短路径节点,将其标记为确定最短路径,并更新其相邻节点的距离。 3.重复步骤2直到终点被标记为确定最短路径或者所有节点均被标记为确定最短路径。 4.输最短路径。 使用邻接表可以较为简便地实现Dijkstra算法,具体实现如下: ```c++ #include<bits/stdc++.h> using namespace std; const int MAXN=100010; const int INF=0x3f3f3f3f; struct Edge{ int to,w; }; vector<Edge> G[MAXN]; int dis[MAXN]; bool vis[MAXN]; void dijkstra(int s){ priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q; memset(dis,INF,sizeof(dis)); memset(vis,false,sizeof(vis)); dis[s]=0; q.push(make_pair(0,s)); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=true; for(int i=0;i<G[u].size();i++){ int v=G[u][i].to; int w=G[u][i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; q.push(make_pair(dis[v],v)); } } } } int main(){ int n,m,s,t; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); G[u].push_back((Edge){v,w}); G[v].push_back((Edge){u,w}); } dijkstra(s); printf("%d\n",dis[t]); return 0; } ``` 其中,邻接表G[u]表示节点u的相邻节点,Edge结构体表示边的信息,dis[u]表示起点到节点u的最短路径长度,vis[u]表示节点u是否被标记为确定最短路径。使用优先队列STL来维护节点距离的更新,pair<int,int>表示节点距离和节点编号,greater<pair<int,int>>表示节点距离的比较器,使得距离小的节点在队列前面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值