洛谷 P1918 保龄球

题目链接:

https://www.luogu.com.cn/problem/P1918icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1918


题目:

DL 算缘分算得很烦闷,所以常常到体育馆去打保龄球解闷。因为他保龄球已经打了几十年了,所以技术上不成问题,于是他就想玩点新花招。

DL 的视力真的很不错,竟然能够数清楚在他前方十米左右每个位置的瓶子的数量。他突然发现这是一个炫耀自己好视力的借口——他看清远方瓶子的个数后从某个位置发球,这样就能打倒一定数量的瓶子。

1. ◯ ◯ ◯

2. ◯ ◯ ◯ ◯

3. ◯

4. ◯ ◯

如上图,每个 “◯” 代表一个瓶子。如果 DL 想要打倒 3 个瓶子就在 1 位置发球,想要打倒 4 个瓶子就在 2 位置发球。

现在他想要打倒 m 个瓶子。他告诉你每个位置的瓶子数,请你给他一个发球位置。

输入格式:

第一行包含一个正整数 n,表示位置数。

第二行包含 n 个正整数 eq?a_%7Bi%7D​ ,表示第 i 个位置的瓶子数,保证各个位置的瓶子数不同。

第三行包含一个正整数 Q,表示 DL 发球的次数。

第四行至文件末尾,每行包含一个正整数 m,表示 DL 需要打倒 m 个瓶子

输出格式:

共 Q 行。每行包含一个整数,第 i 行的整数表示 DL 第 i 次的发球位置。若无解,则输出 0。

例:

输入:

5
1 2 4 3 5
2
4
7

输出:

3
0

数据范围:

对于 100% 的数据,1 ≤ n , Q ≤ 100000 , 1 ≤ eq?a_%7Bi%7D , m ≤ eq?10%5E%7B9%7D 。


思路:

首先观察题目,题目目标是为了恰好打倒m个瓶子,需要在哪一行发球。

那么需要将行号与该行瓶子数量相绑定。那我们可以定义一种结构体node,其中含有当前行数fir与当前行的瓶子数量num,用于存储哪一行有多少个瓶子。

接着我们按照题意,首先使用整形变量n来接收有多少行,接着循环n次将行数与瓶子数量存入结构体数组arr(我的ac代码中使用vector,因为n只有最小值并且最大值不确定)。

在此处思考一个问题,若我们使用循环来遍历结构体数组来寻找目标值,那么该动作的时间复杂度为O(n),当数据量极大时,我们寻找一次的时间就可能超过限定时间了。所以我们选择时间复杂度较低的搜索算法——二分算法(时间复杂度为O(Log n))来搜索目标行数。

然后对结构体数组进行按瓶子数量排序(因为二分查找需要在一个有序数组中进行)。接着每次接收玩目标瓶子数后,对排序后的数组进行二分查找。

在二分查找后,判断arr[l].num是否与目标瓶数相等。若相等,则输出arr[l].fir;反之,输出0。


AC代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

#define ll long long

struct node {
	ll fir, num;
};

ll n, num, tar;
vector<node> arr;

bool cmp(node x, node y) {
	return x.num < y.num;
}

int main() {

	scanf("%lld", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &num);
		arr.push_back({ i,num });
	}
	sort(arr.begin(), arr.end(), cmp);
	scanf("%lld", &n);
	while (n--) {
		scanf("%lld", &tar);
		ll l = 0, r = arr.size();
		while ((l + 1) < r) {
			ll mid = (l + r) >> 1;
			if (arr[mid].num > tar) {
				r = mid;
			}
			else {
				l = mid;
			}
		}
		if (arr[l].num == tar) {
			printf("%lld\n", arr[l].fir);
		}
		else {
			printf("0\n");
		}
	}

	return 0;
}

如果我的文章对你有所帮助,不妨给我个关注何点赞吧。

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值