当前位置: 首页 > article >正文

信息学奥赛一本通 1422:【例题1】活动安排

【题目链接】

ybt 1422:【例题1】活动安排

【题目考点】

1. 贪心

【解题思路】

该题属于区间选点问题,ybt 1324:【例6.6】整数区间 是给定一些区间,选择一些点使得每个区间范围内至少有1个点。
本题为:给定一些区间,选择一些点使得每个区间范围内至少有给定数量的点。
本题中,每个地块可以看作数轴上的一个点,“B和E之间最少种T棵树”,指的是在区间[B, E]中至少选择T个点。

贪心选择:对每个区间,如果还需要选点,则尽量从区间右侧选点。
所有区间按照右端点 E E E从小到大排序,排序后第i区间范围为 [ B i , E i ] [B_i, E_i] [Bi,Ei],其中需要至少选择 T i T_i Ti个点。
贪心选择的具体解释:

  • 如果该区间中已经选择点的数量大于等于 T i T_i Ti,则不需要再选点。
  • 如果该区间中已经选择点的数量小于 T i T_i Ti,则从 E i E_i Ei B i B_i Bi从大到小遍历每个点,遇到未选择的点就选择该点,直到选够 T i T_i Ti个点。

贪心选择性质的证明:

  1. 证明存在最优解包含第一次的贪心选择
    第一次的贪心选择为:在 [ B 1 , E 1 ] [B_1,E_1] [B1,E1]中选择区间 [ E 1 − T 1 + 1 , E 1 ] [E_1-T_1+1, E_1] [E1T1+1,E1]中的整数点,共 T 1 T_1 T1个点。
    反证法:假设所有最优解都不包含第一次的贪心选择
    也就是说在 [ B 1 , E 1 − T 1 ] [B_1, E_1-T_1] [B1,E1T1]选择了一些点,而 [ E 1 − T 1 + 1 , E 1 ] [E_1-T_1+1, E_1] [E1T1+1,E1]中存在未被选择的点。设在 [ B 1 , E 1 − T 1 ] [B_1, E_1-T_1] [B1,E1T1]中选择了点 G G G,而 [ E 1 − T 1 + 1 , E 1 ] [E_1-T_1+1, E_1] [E1T1+1,E1]中点 F F F未被选择。
    现在不选择点 G G G转而选择点 F F F,第2、第3等后面的区间中已选择的点可能会增加,也可能不变。每个区间的选点数量都满足要求,总选点数量不变。因此这样变换选择点后,此时的选点方案仍然是最优解。
    将在 [ B 1 , E 1 − T 1 ] [B_1, E_1-T_1] [B1,E1T1]中选择的所有点都去掉,转而在 [ E 1 − T 1 + 1 , E 1 ] [E_1-T_1+1, E_1] [E1T1+1,E1]中选择相同数量的点,最后选择的点就是 [ E 1 − T 1 + 1 , E 1 ] [E_1-T_1+1, E_1] [E1T1+1,E1]中的每个点,这也就是进行了贪心选择,这个包含了第一次贪心选择的解仍然是最优解。这与假设相悖,原命题得证。
  2. 假设最优解包含前k次的贪心选择,证明存在最优解包含第k+1次的贪心选择:
    证明过程与第1点的证明过程相似,不再赘述。

具体实现:
设vis数组,vis[i]表示第i点是否已被选择。
对于每个区间,先遍历整个区间,统计出已选点数量,也就是求出待选择点的数量。

  • 如果待选择点的数量为0,就看下一个区间。
  • 否则,从区间右端点开始,向前遍历,遇到未选择的点,就选择一个点,直到待选择点的数量为0。统计选点总数量,就是问题结果。

【题解代码】

解法1:贪心

#include<bits/stdc++.h>//贪心选择性质:按区间右端点升序排序,每个区间尽量选后面的数字 
using namespace std;
struct Node
{
	int b, e, t;//[b, e]中选t个数 
};
bool cmp(Node a, Node b)
{
	return a.e < b.e;
}
bool vis[30005];//vis[i]:点i是否已被选择 
int main()
{
	Node a[5005];
	int n, m, ans = 0;//ans:总选点数量 
	cin >> n >> m;
	for(int i = 1; i <= m; ++i)
		cin >> a[i].b >> a[i].e >> a[i].t; 
	sort(a+1, a+1+m, cmp);
	for(int i = 1; i <= m; ++i)
	{
		for(int j = a[i].b; j <= a[i].e; ++j) 
			if(vis[j] && --a[i].t <= 0)//a[i].t这里表示区间中还需要选择几个点
				break;
		if(a[i].t <= 0) 
			continue;
		for(int j = a[i].e; j >= a[i].b; --j) if(!vis[j])
		{
			vis[j] = true;
			ans++;
			if(--a[i].t <= 0)
				break;
		}
	}
	cout << ans;
	return 0;
}

http://www.kler.cn/a/530418.html

相关文章:

  • 蓝桥杯备赛经验帖
  • 计算机网络——流量控制
  • 记录 | 基于MaxKB的文字生成视频
  • deep generative model stanford lecture note2 --- autoregressive
  • React
  • 三、js笔记
  • Vue.js 的介绍与组件开发初步
  • CSS Display属性完全指南
  • Shell基础:中括号的使用
  • React基础知识回顾详解
  • Java基础知识总结(三十九)--File类
  • 常见计算机视觉算法介绍
  • 全面解析机器学习优化算法中的进化策略
  • Baklib如何改变内容管理平台的未来推动创新与效率提升
  • SQLAlchemy ORM在Python Web开发中的核心作用探究
  • c语言:编译和链接(详解)
  • 点击WPS 任务栏上的图标,不是马上进入工作页面,而是呈现多个文档页面选择时的处理方法
  • Ollama+OpenWebUI部署本地大模型
  • LeetCode题练习与总结:有效三角形的个数--611
  • java练习(4)
  • 智慧城市(城市大脑)建设方案
  • 后台管理系统通用页面抽离=>高阶组件+配置文件+hooks
  • 基于YOLO11的遥感影像山体滑坡检测系统
  • 【深度学习】DeepSeek模型介绍与部署
  • Vue 3 30天精进之旅:Day 12 - 异步操作
  • 科技快讯 | OpenAI首次向免费用户开放推理模型;特朗普与黄仁勋会面;雷军回应“10后小学生深情表白小米SU7”