贪心算法OJ刷题(2)

news/2024/7/5 8:53:40

多机调度问题

题目描述

某工厂有n个独立的作业,由m台相同的机器进行加工处理。作业i所需的加工时间为 t i t_i ti,任何作业在被处理时不能中断,也不能进行拆分处理。现厂长请你给他写一个程序:算出n个作业由m台机器加工处理的最短时间。

输入:第一行T(1<T<100)表示有T组测试数据。每组测试数据的第一行分别是整数n,m(1<=n<=10000,
1<=m<=100),接下来的一行是n个整数ti(1<=t<=100)。输出:所需的最短时间。

解题思路

该题较难求出最优解,但可以利用贪心策略设计出次优解。

分情况讨论:1、当n<=m时,只要将作业分给每一个机器即可;

2、当n>m时,首先将n个作业从大到小排序,然后依此顺序将作业分配给空闲的处理机。也就是说从剩下的作业中,选择需要处理时间最长的,给最先结束的机器去执行,然后依次选择处理时间次长的,直到所有的作业全部处理完毕,或者机器不能再处理其他作业为止。

如果我们每次是将需要处理时间最短的作业分配给空闲的机器,那么可能就会出现其它所有作业都处理完了只剩所需时间最长的作业在处理的情况,这样势必效率较低。

#pragma once
#include<iostream>
#include<algorithm>
#include <vector>
using namespace std;

bool cmp(const int& x, const int& y)
{
	return x > y;
}

int findMax(const vector<int>& machines)
{
	int max = machines[0];
	for (int i = 1; i < machines.size(); ++i)
	{
		if (machines[i] > max) max = machines[i];
	}
	return max;
}

int greedStrategy(vector<int>& works, vector<int>& machines)
{
	//按照作业时间从大到小排序 sort默认升序
	sort(works.begin(), works.end(), cmp);
	int workNum = works.size();
	int machineNum = machines.size();
	// 作业数如果小于机器数,直接返回最大的作业时间
	int minimalTime = 0;
	if (workNum <= machineNum) {
		minimalTime = works[0];
	}
	else
	{
		for (int i = 0; i < workNum; ++i)
		{
			int id = 0;
			// 第一个任务给第一台机器
			int tmp = machines[id];
			// 从剩余机器中选择作业时间最小的
			for (int j = 1; j < machineNum; ++j)
			{
				if (machines[j] < tmp)
				{
					id = j;
					tmp = machines[j];
				}
			}
			// 将当前作业交给最小的机器执行
			machines[id] += works[i];
		}
		minimalTime = findMax(machines);
	}
	return minimalTime;
}

活动选择

题目描述

有n个需要在同一天使用同一个教室的活动a1, a2, …, an,教室同一时刻只能由一个活动使用。每个活动a[i]都有一个开始时间s[i]和结束时间f[i]。一旦被选择后,活动a[i]就占据半开时间区间[s[i],f[i])。如果[s[i],f[i])和[s[j],f[j])互不重叠,a[i]和a[j]两个活动就可以被安排在这一天。求使得尽量多的活动能不冲突的举行的最大数量。

解题思路

选择持续时间最短的活动(其开始和结束时间都无法让其他活动在同一天举办)或者选择活动时间最早开始的活动(其持续时间可能最长)都无法得到最优解。

直入主题,每次选择活动结束时间最早的活动 a i a_i ai,只要它的活动开始时间不早于上一个活动的结束时间,就可以选择该活动。故将活动结束时间进行升序处理,然后遍历活动开始时间,选开始时间最早的活动。

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;

int ActivitySelect(vector<int>& start, vector<int>& end)
{
	map<int, int> map;
	int ret = 0;
	for (size_t i = 0; i < start.size(); ++i)
	{
		map.insert(pair<int, int>(end[i], start[i]));
	}
	int maxtime = 0;
	for (auto& kv : map)
	{
		//如果该活动的开始时间不小于目前的结束时间,就可以选择
		if (maxtime <= kv.second)
		{
			//更新最晚结束时间
			maxtime = kv.first;
			ret++;
		}		
	}
	return ret;
}

最多可以参加的会议数

题目描述

给你一个数组 events,其中 events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi 。你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i 。注意,一天只能参加一个会议。请你返回你可以参加的 最大 会议数目。

解题思路

此题是在区间内任选一天参加即可。比如[[1,2], [1,2]],可以在第一天参加第一个会议,在第2天参加第二个会议。

用unorder_map,第一个参数int保存会议的开始时间,第二个参数vector保存在同一天开始的会议的结束时间。再用priority_queue对同一天开始的会议的结束时间做升序排列,每次选择最早结束的会议。

class Solution {
public:
    int maxEvents(vector<vector<int>>& events) {
        int maxDay = 0;
        unordered_map<int, vector<int>> day;
        for(vector<int>& event : events)
        {
            //遍历得到最晚的结束时间
            if(maxDay < event[1]) maxDay = event[1];
            //开始时间相同的会议,对应的结束时间存在一个数组里
            day[event[0]].push_back(event[1]);
        }

        int ret = 0;
        //构建一个小顶堆
        priority_queue<int, vector<int>, greater<int>> q;
        for(int i = 1; i <= maxDay; ++i)
        {
            //第1天到第maxDay天之间,看哪天有会议
            if(day.find(i) != day.end())
            {
                //记录会议的结束时间,升序排列
                for(int end_time : day[i])
                {
                    q.push(end_time);
                }
            }
            // 删除队列里结束时间小于i的会议:因为它们已经结束了,无法再选择
            while(!q.empty() && q.top() < i)
            {
                q.pop();
            }
            // 每次选择一个最早结束的会议,然后pop掉
            if(!q.empty())
            {
                q.pop();
                ret++;
            }
        }
        return ret;
    }
};

无重叠区间

题目描述

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

解题思路

和活动选择这道题思路一样,只不过返回结果不一样。

也可以按照左区间来递增排序,选择右区间更小的那个(可以为后序的预留更大的空间)

//沿用活动选择的思路
class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b)
    {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        int n = intervals.size();//总共有n个区间
        //按照右区间进行升序排列
        sort(intervals.begin(), intervals.end(), cmp);
        int max_right = INT_MIN;
        int can = 0;
        for(int i = 0; i < n; ++i)
        {
            //如果左区间大于等于最大右区间
            //表示两个区间无重叠
            if(intervals[i][0] >= max_right)
            {
                max_right = intervals[i][1];
                can++;
            }
        }
        return n - can;
    }
};
//以左区间来作升序排序
class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b)
    {
        return a[0] < b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        int n = intervals.size();//总共有n个区间
        //按照左区间进行升序排列
        sort(intervals.begin(), intervals.end(), cmp);
        
        int end = intervals[0][0], prev = 0, count = 0;        
		for (int i = 1; i < intervals.size(); i++) 
        {
            //如果当前区间的左区间 大于等于 累积区间的右区间,表示无重叠
            if (intervals[i][0] >= intervals[prev][1]) 
            {
                prev = i;
            } 
            //表示两个区间有重叠
            else 
            {                
                //选择结束时间早的那个右区间
                if (intervals[prev][1] > intervals[i][1]) 
                {
                    prev = i;
                }
                count++;
            }
		}
        return count;
    }
};

注意:二维数组vector<vector<int>>要用算法函数sort(默认是按第一个元素升序排列),用到的比较函数是cmp,其中的单个元素是vector<int>

bool cmp(const vector<int>& kv1, const vector<int>& kv2)
{
    return kv1[1] < kv2[1];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
}

http://lihuaxi.xjx100.cn/news/1087685.html

相关文章

PromQL,让你轻松实现监控可视化!快来了解一下吧!

Prometheus 中的一些关键设计&#xff0c;比如注重标准和生态、监控目标动态发现机制、PromQL等。 PromQL 是 Prometheus 的查询语言&#xff0c;使用灵活方便&#xff0c;但很多人不知道如何更好利用它&#xff0c;发挥不出优势。 PromQL主要用于时序数据的查询和二次计算场…

【热门框架】Maven依赖传递,可选依赖以及排除依赖指的是什么?有什么意义?

Maven依赖传递是指当一个项目依赖另一个项目时&#xff0c;Maven会自动下载并构建这些依赖项&#xff0c;同时还会将这些依赖项所依赖的其他项一并下载并构建。这个过程会一直递归下去&#xff0c;直到所有依赖的项都被下载并构建完成。这个过程就称为依赖传递。 依赖传递可以…

【MySQL】函数和约束

如标题所说,本文重点只有两个:MySQL语句里面的函数和约束 目录 1. 函数1.1 字符串函数1.2 数值函数1.3 日期函数1.4 流程函数 2.约束2.1 外键的删除更新行为 1. 函数 因为在前一篇文章里面有讲到聚合函数,所以在这里就不重复介绍了,本文所介绍的函数有4类:字符串函数,数值函数…

数据结构与算法之数组: Leetcode 605. 种花问题 (Typescript版)

种花问题 https://leetcode.cn/problems/can-place-flowers/ 描述 假设有一个很长的花坛&#xff0c;一部分地块种植了花&#xff0c;另一部分却没有。可是&#xff0c;花不能种植在相邻的地块上&#xff0c;它们会争夺水源&#xff0c;两者都会死去。 给你一个整数数组 flo…

Java 中的线程是什么,如何创建和管理线程-中(十二)

书接上文 三、Java 线程的同步 Java 中的线程同步是通过 synchronized 关键字实现的。在多线程环境下&#xff0c;当多个线程同时访问共享资源时&#xff0c;如果不进行同步控制&#xff0c;就会出现数据不一致、死锁等问题。为了保证多个线程之间的安全访问&#xff0c;需要…

MCU固件升级系列1(STM32)

本系列将从升级流程、boot代码编写、APP代码编写以及固件打包来介绍&#xff0c;硬件选用STM32F407ZGT6&#xff08;手里只有&#xff09;&#xff0c;来完成这系列教程。 前言 为什么需要固件升级: 功能更新&#xff1a;随着产品的迭代和用户需求的变化&#xff0c;可能需要…

Prometheus监控系统存储容量优化攻略,让你的数据安心保存!

云原生监控领域不可撼动&#xff0c;Prometheus 是不是就没缺点&#xff1f;显然不是。 一个软件如果什么问题都想解决&#xff0c;就会导致什么问题都解决不好。所以Prometheus 也存在不足&#xff0c;广受诟病的问题就是 单机存储不好扩展。 1 真的需要扩展容量吗&#xff…

04_Uboot操作命令与其他命令

目录 BOOT 操作命令 bootz命令 bootm 命令 reset 命令 go 命令 run 命令 mtest 命令 BOOT 操作命令 uboot的本质工作是引导Linux,所以uboot肯定有相关的boot(引导)命令来启动Linux。常用的跟boot有关的命令有:bootz、bootm和boot。 bootz命令 要启动Linux,需要先将Lin…