洛谷P1083 借教室 (差分数组,二分)

一.题目

# P1083 [NOIP 2012 提高组] 借教室

## 题目描述

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来 $n$ 天的借教室信息,其中第 $i$ 天学校有 $r_i$ 个教室可供租借。共有 $m$ 份订单,每份订单用三个正整数描述,分别为 $d_j,s_j,t_j$,表示某租借者需要从第 $s_j$ 天到第 $t_j$ 天租借教室(包括第 $s_j$ 天和第 $t_j$ 天),每天需要租借 $d_j$ 个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 $d_j$ 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 $s_j$ 天到第 $t_j$ 天中有至少一天剩余的教室数量不足 $d_j$ 个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

## 输入格式

第一行包含两个正整数 $n,m$,表示天数和订单的数量。

第二行包含 $n$ 个正整数,其中第 $i$ 个数为 $r_i$,表示第 $i$ 天可用于租借的教室数量。

接下来有 $m$ 行,每行包含三个正整数 $d_j,s_j,t_j$,表示租借的数量,租借开始、结束分别在第几天。

每行相邻的两个数之间均用一个空格隔开。天数与订单均用从 $1$ 开始的整数编号。

## 输出格式

如果所有订单均可满足,则输出只有一行,包含一个整数 $0$。

否则(订单无法完全满足)输出两行,第一行输出一个负整数 $-1$,第二行输出需要修改订单的申请人编号。

## 输入输出样例 #1

### 输入 #1

```
4 3 
2 5 4 3 
2 1 3 
3 2 4 
4 2 4
```

### 输出 #1

```
-1 
2
```

## 说明/提示

【输入输出样例说明】

第 $1$ 份订单满足后,$4$ 天剩余的教室数分别为 $0,3,2,3$。第 $2$ 份订单要求第 $2$ 天到第 $4$ 天每天提供 $3$ 个教室,而第 $3$ 天剩余的教室数为 $2$,因此无法满足。分配停止,通知第 $2$ 个申请人修改订单。

【数据范围】

对于 $10\%$ 的数据,有 $1\le n,m\le 10$;

对于 $30\%$ 的数据,有 $1\le n,m\le 1000$;

对于 $70\%$ 的数据,有 $1 \le n,m \le 10^5$;

对于 $100\%$ 的数据,有 $1 \le n,m \le 10^6$,$0 \le r_i,d_j\le 10^9$,$1 \le s_j\le t_j\le n$。


NOIP 2012 提高组 第二天 第二题

2022.2.20 新增一组 hack 数据

二.解法思路

首先我们可以自然的想到用暴力来解,但暴力解下来时间复杂度是明显超过题目要求的,所以我们要转换思路,可以明显发现我们要对接教室的开始区间和结束区间进行一样操作,所以我们就可以考虑前缀和,然后我们要找的是能满足要求的,所以采用差分数组。换者而言,因为我们一个一个对区间中的数据进行操作太慢了,所以我们采取差分数组对里面的整个进行操作,又因为要求找到不合符的地方,所以我们可以用二分法找到不符合要求而区域,然后不断进行二分,找到第一个不符合要求的区域

三.二分细节

首先二分法的细节非常重要,很多时候如果不注意会导致死循环,这里一般分为两种

1.找第一个满足条件的

 begin<end;

mid= (begin+end);begin=mid+1;end=mid;

2.找最后一个一个满足条件的

begin<end;

mid= (begin+end+1);begin=mid;end=mid-1;

四.本体题解

#include<iostream>
#include <cstring>
using namespace std;

long long diff[1000011], l[1000011],  r[1000011],  need[1000011], rest[1000011], d[1000011];
int n, m;
int IsOk(int x)
{
	memset(diff, 0, sizeof(diff));
	for (int i = 1; i <= x; i++)
	{
		diff[l[i]] += d[i];
         diff[r[i]+1] -= d[i];//下一天释放
	}
	for (int i = 1; i <= n; i++)
	{
		need[i] = need[i - 1] + diff[i];
		if (need[i] - rest[i] > 0)return 0;
	}
	return 1;
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> rest[i];
	for (int i = 1; i <= m; i++)cin >> d[i] >> l[i] >> r[i];
	int s = 1, end = m;
	if (IsOk(m)) { cout << 0; return 0; }//符合直接输出结果
	while (s < end) {//不符合进入循环开始判断
	int	mid = (s + end) / 2;
	if (IsOk(mid))s = mid + 1;// 1代表前面都符合,到后面开始寻找
	else end = mid;//返回0代表后面都不合符,往前寻找第一个不符合的
   }
	cout << -1 << endl << end;
	return 0;
}

### 关于 P1083 的 Java 实现与解题思路 对于上的问题 P1083,该题目通常涉及特定算法的应用。虽然提供的参考资料未直接提及此题目[^1],可以从相似类型的题目中推断出可能的解决方案。 #### 题目分析 假设 P1083 是一个典型的动态规划或者贪心算法类的问题,则解决此类问题的关键在于理解输入数据的特点以及如何有效地利用这些特点来构建最优解路径。如果涉及到序列处理或区间操作,那么定义状态转移方程将是核心部分之一。 #### 动态规划方法概述 当面对具有重叠子问题特性的优化问题时,采用自底向上的方式逐步求解较小规模的问题,并将其结果存储起来用于更大规模问题的解答是一种常见策略。这有助于减少重复计算并提高效率。 #### 贪心算法适用场景 对于某些情况下能够通过局部最优选择达到全局最优点的问题来说,每次做出当前看来最好的决定可能是有效的途径。然而,在应用之前需证明这种做法确实能得出整体最佳方案。 #### 示例代码框架 (基于猜测) 由于具体细节未知,下面给出一段简化版伪代码作为参考: ```java import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 初始化变量读取输入 // 处理逻辑:根据实际需求调整此处的具体实现 // 输出最终答案 System.out.println(result); } } ``` 为了提供更精确的帮助,请查阅官方文档或其他可靠资源获取针对 P1083 更详细的描述和提示信息。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值