腾讯校园招聘笔试 2019-8-17 第四题 另一种解法

之前在博客https://blog.csdn.net/hanzhen7541/article/details/99710954中我们讨论了一种单调栈的解法,复杂度是O(nlogn)。那么实际上还有一种解法,用的是动态规划+二分查找。

首先看一下题目:

小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。

小Q从第一栋一直走到了最后一栋,小Q从来没有看到过这么多高楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)

输入描述:

输入第一行将包含一个数字n,表示楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),代表一栋楼的高度。

1<=n<=100000;

1<=wi<=100000;

输出描述:

输出一行,包含空格分隔的m个数字vi,分别代表小Q在第i栋楼的时候能看到的楼的数量。
 

对于这类最长的下降(上升)子序列问题,动态规划是一种非常好的办法。解题思路是:首先设dp1[i-1]是一个存放第i个位置往左看能看到多少高楼的数组(dp1[0]就是1,表示从1位置往左看一定能看到最左边这个楼)。dp3[i]维护了一个下降的队列,对于每一个height[i],都应该在dp3的某一个位置pos,这个位置pos是队列中第一个比height[i]小或者等于的位置,把它插入进去。

同时我们维护一个变量tmp,这个变量非常关键,表示的是每次比较从dp3[0]比较到dp[tmp]之间。为什么要维护这个变量呢?我们举个例子,假如说之前dp3维护的队列高度是8,5,3,1这样一个队列,假设下一个楼的高度是6,那么它将找到pos=1也就是高度是5的位置,将5替换成6. 同时,将tmp记录为pos也就是1的位置。那么对于下一个数字来说,假如是2,他显然不能在所有的8,6,3,1这个序列中比较,因为前一个数字5已经把3,1这两个楼的高度“挡住了”,所以这个tmp实际上维护了一个查找的范围,保证只能在0~tmp这些索引里面来找,从而起到忽略已经被挡住的楼的作用。

我们来看一下代码:

int main(){
	
	int n;
	cin >> n;
	vector<int>x(n);
	vector<int>dp1(n, 0);
	vector<int>dp2(n, 0);
	for (int i = 0; i<n; i++) cin >> x[i];
	vector<int>dp3(n, 0);
	int tmp = 0;
	//dp1[i-1]表示从第i个位置向左看能看到的高楼的个数,tmp表示只看从0到tmp之间的楼,因为一个比较高的楼会挡住前面比较矮的楼
	for (int i = 0; i<n; i++){
		int pos = lower_bound(dp3.begin(), dp3.begin() + tmp + 1, x[i], greater<int>()) - dp3.begin();
		/*如果x[i]比所有序列值都小,那么pos返回tmp+1,也就是下面if语句第一句,从物理意义上来说,直接把他接在下降序列的末尾就可以了*/
		if (pos>tmp) tmp++;
		/*如果在0~tmp里面能找到一个值,那么tmp就取成当前的pos值,对于后面的i来说,只需要查找0~tmp里面的值,tmp索引以后的较小值相当于被忽略了*/
		else tmp = pos;
		dp3[pos] = x[i];
		dp1[i] = pos+1;
	}
	dp3.clear();
	dp3.resize(n, 0);
	tmp = 0;
	for (int i = n - 1; i >= 0; i--){
		int pos = lower_bound(dp3.begin(), dp3.begin() + tmp + 1, x[i], greater<int>()) - dp3.begin();
		if (pos>tmp)tmp++;
		else tmp = pos;
		dp3[pos] = x[i];
		dp2[i] = pos+1;
	}
	for (int i = 0; i<n; i++){
		int ct = 1 + (i == 0 ? 0 : dp1[i - 1]) + (i == n - 1 ? 0 : dp2[i + 1]);
		cout << ct << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

相关阅读:最长上升子序列:https://blog.csdn.net/hanzhen7541/article/details/99721688

                  upper_bound() 用法 :https://blog.csdn.net/hanzhen7541/article/details/99722948

                 另一种解法: https://blog.csdn.net/hanzhen7541/article/details/99710954

 

我觉得这道题很难想。自己很菜,还希望和大家多多讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值