P13750 [NWERC 2024] Limited Library

传送门

题目描述

在暑假期间,校园里居住的学生较少,因此这是为代尔夫特理工大学图书馆增添新书的绝佳时机。这些新书的宽度都相同,但高度各不相同。由于现有的书架都已满,图书馆管理委员会决定新增一个书架来展示这些新书。

新书架有若干层,每层高度不同。每层最多可放 x x x 本书。由于可能会有剩余空间,管理委员会还希望在书架上展示一些艺术品,每层最多放一个艺术品。只有当该层旁边最多有 y y y 本书时,艺术品才能放下,因为艺术品占据与 x − y x-y x−y 本书相同的空间。例如,图 L.1 展示了一个书架,其中有三层可以放置艺术品。

管理委员会希望你找出,在能够放下所有新书的前提下,最多能在多少层上放置艺术品。

输入格式

输入包括:

  • 一行四个整数 n n n、 m m m、 x x x、 y y y( 1 ≤ n , m ≤ 10 5 1 \leq n, m \leq 10^5 1≤n,m≤105, 1 ≤ y < x ≤ 1000 1 \leq y < x \leq 1000 1≤y<x≤1000),分别表示书架的层数、新书的数量、每层最多可放的书本数、每层与艺术品并排时最多可放的书本数。
  • 一行 n n n 个整数 a a a( 1 ≤ a ≤ 10 9 1 \leq a \leq 10^9 1≤a≤109),表示每层的高度。
  • 一行 m m m 个整数 b b b( 1 ≤ b ≤ 10 9 1 \leq b \leq 10^9 1≤b≤109),表示每本书的高度。

输出格式

如果能将 m m m 本书全部放入 n n n 层书架中,输出最多能放置的艺术品数量。否则,输出" impossible \texttt{impossible} impossible"。

输入输出样例 #1

输入 #1

复制代码
4 8 4 2
4 8 6 2
1 2 3 5 7 7 8 8

输出 #1

复制代码
3

输入输出样例 #2

输入 #2

复制代码
4 11 3 2
2 2 2 2
1 1 1 1 1 1 1 1 1 1 1

输出 #2

复制代码
1

输入输出样例 #3

输入 #3

复制代码
2 10 3 2
8 6
4 2 1 3 6 2 1 3 4 5

输出 #3

复制代码
impossible

输入输出样例 #4

输入 #4

复制代码
3 8 8 3
7 9 4
2 3 4 5 6 7 8 9

输出 #4

复制代码
3

题意

现在有 n n n 层书架,每层最多可放 x x x 本书,又有 m m m 本新书要放进书架里,且在放书时放的书的高度不得超过该层书架高度。

同时我们希望能放更多艺术品,每层最多放 1 1 1 个艺术品,且只有当该层旁边最多有 y y y 本书时,艺术品才能放下。

求在能够放下所有新书的前提下,最多能放多少艺术品,若新书都无法放完,则输出 impossible

思路

因为求的是放的艺术品数,所以可以用二分来求艺术品数(因为若放 k k k 个艺术品可行,那么放更少的留的空间还会更大,一样可行,满足二分单调性)。

而且因为每层能放的书的数量是一样的,矮书既能放高层又能放矮层,但高书只能放高层,所以最优策略为矮书放矮层,高书放高层。

同理,艺术品放矮层比放高层也要更优。

所以先对每本书和每层书架的高度都升序排序。

接着二分答案,check 中在前 m i d mid mid 层放 y y y 本书,其他层放 x x x 本书,最后看是否放完所有书即可。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,x,y,l,r,ans=-1;//先为答案赋值为 -1,若最后还是 -1 则表示一直都没合法过,输出 impossible
vector<int> a,b;
bool check(int k)
{
	int j=0;
	for(int i=0;i<k&&j<m;i++)//在前 mid 层放 y 本书 
	{
		int cnt=y;
		while(cnt--&&j<m&&b[j]<=a[i])
			j++;
	}
	for(int i=k;i<n&&j<m;i++)//在其他层放 x 本书
	{
		int cnt=x;
		while(cnt--&&j<m&&b[j]<=a[i])
			j++;
	}
	return j>=m;//判断书是否放完
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>x>>y;
	r=n;
	a.resize(n);
	b.resize(m);
	for(int i=0;i<n;i++)
		cin>>a[i];
	for(int i=0;i<m;i++)
		cin>>b[i];
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());//排序
	while(l<=r)//二分答案
	{
		int mid=(l+r)>>1;
		if(check(mid))
			ans=mid,l=mid+1;
		else 
			r=mid-1;
	}
	if(ans==-1)//一直都放不完
		cout<<"impossible";
	else
		cout<<ans;
	return 0;
}
相关推荐
cpp_250112 小时前
P2249 【深基13.例1】查找
数据结构·c++·算法·题解·二分·洛谷
叶小鸡2 天前
小鸡玩算法-力扣HOT100-贪心算法
算法·leetcode·贪心算法
小辉同志3 天前
45. 跳跃游戏 II
c++·leetcode·游戏·贪心算法
qeen874 天前
【算法笔记】二分查找与二分答案
c语言·c++·笔记·学习·算法·二分
空中海4 天前
Kubernetes 生产实践、可观测性与扩展入门
java·贪心算法·kubernetes
khalil10204 天前
代码随想录算法训练营Day-31贪心算法 | 56. 合并区间、738. 单调递增的数字、968. 监控二叉树
数据结构·c++·算法·leetcode·贪心算法·二叉树·递归
贾斯汀玛尔斯7 天前
每天学一个算法--贪心算法(Greedy Algorithm)
算法·贪心算法
算法鑫探7 天前
贪心算法(C 语言实现)及经典应用
c语言·数据结构·算法·贪心算法
QC·Rex7 天前
Kubernetes 生产环境调试安全最佳实践:2026 年完整指南
安全·贪心算法·kubernetes