一箭多雕
时间限制: 1 sec 内存限制: 128 MB
题目描述
小明喜欢武侠小说,在武侠世界里,他不但练就了一箭双雕的能力,还可以一箭多雕。
现在所有雕在一条直线上从左到右排列,但是他们的高度不同。而小明想要把他们都射下来。小明使用的是一种特殊的弓箭,他可以将弓箭射到任意一个高度为H的雕,当射中一个高度为H的雕后,弓箭的高度会下降到H-1,再从左到右飞行,直到射到高度为H-1的大雕,再降低1的高度,直到飞出大雕的队列。
由于弓箭数量有限,小明想要知道,最少用多少的弓箭,就可以射下所有的大雕。
输入
第一行一个整数,n表示直线上的大雕数量。
第二行n个整数,表示从左到右每个大雕的高度。
输出
输出一个整数,表示最少用的弓箭数量。
样例输入
【样例1】
5
2 1 5 4 3
【样例2】
5
1 2 3 4 5
【样例3】
5
4 5 2 1 4
样例输出
【样例1】
2
【样例2】
5
【样例3】
3
数据范围
30%的数据,n<=5000, 大雕的高度<=10000
70%的数据,n<=1000000, 大雕的高度<=1000000
100%的数据,n<=1000000, 大雕的高度<=1000000000。
思路
第一眼看到的时候,想当然的以为这道题应该是读入全部数据后,从头开始遍历大雁高度,之后从当前位置查找有没有h-1高度的大雁,存在则继续向后找,不存在则箭数++。这样的思路没有问题,但如果选择生硬的使用STL解决,回避不了TLE的命运。
(如果的确有用STL的AC代码还烦请指点一下)
看了一篇博客中的代码后思路清晰了一些,为代码补全了一些注释与自己的理解。
1e9的数据,1e6的询问,应该做下离散化,之后维护了一个有关同一高度的箭编号的链表(毕竟大雁的高度是可以重复的),这段读了很久,所以维护链表的每行代码都添加了注释。
AC代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n, x, h, now, ans;
int a[1000005], p[1000005], c[1000005];
int head[1000005], Next[1000005];
struct ty {
int v, id;
} b[1000005];
bool cmp(ty x, ty y) {
return x.v < y.v;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i].v = a[i];
b[i].id = i;
}
sort(b + 1, b + n + 1, cmp);
b[0].v = -1e9;
for (int i = 1; i <= n; i++) {
if (b[i].v != b[i - 1].v) x++;//去重 即重复的v不在p数组内分配位置
p[x] = b[i].v; //离散化后的x对应的真实值
c[b[i].id] = x; //c存下每个水平位置所对应的大雁的相对高度(即离散化后的结果)
}
//b数组完成使命
for (int i = 1; i <= x; i++) head[i] = -1;
for (int i = 1; i <= n; i++) {
h = c[i]; //h为第一只大雁的相对高度
if (h + 1 <= x //找相对高度+1没超过最大值x
&& p[h + 1] == a[i] + 1 //这两个相对高度映射回两个真实高度,之间也只差1
&& head[h + 1] != -1) //相对高度h+1高度处有根编号为head[h+1]的箭尚未落下
{
now = head[h + 1]; //用now存储当前这根箭的编号
head[h + 1] = Next[head[h + 1]];
//把h+1高度的其他(另一根)箭的编号放到head[h+1](now当然也是h+1高度上一支箭)
Next[now] = head[h]; //先把h高度已有的箭的标号存储至next[now]中
head[h] = now; //现在变为相对高度为h处有一根编号为now的箭尚未落下
} else {
ans++; //需要额外的一支箭
Next[ans] = head[h]; //先把h高度已有的箭的标号提前转存在next[ans]中
head[h] = ans; //相对高度为h处有一支编号为ans的箭
}
}
cout << ans;
return 0;
}