电梯问题(推公式)

题意描述

一幢 \(n\) 层的大楼有一部电梯停在第一层,它一次最多容纳 \(n-1\) 个人,而且只能在第 \(2\) 层至第 \(n\)
层中某一层停一次。

对于每个人来说,他往下走一层感到 \(1\) 分不满意,往上走一层楼梯感到 \(3\) 分不满意。

现在有 \(n-1\) 个人在第一层,并且他们分别住在第 \(2\) 层至第 \(n\) 层中的每一层,问:电梯停在哪一层时,可以使得这 \(n-1\) 个人不满意的总分达到最小?最小值为多少?

(有些人可以不乘电梯 即直接从第一层走上去)

输入格式

一个数 \(n\) ,表示该大楼有 \(n\)

输出格式

一行两个整数,分别表示停在 第几层 以及最小的 不满意总分

(如果有若干层不满意总分相等,输出层数最低的一层)

输入样例:

33

输出样例:

27 316

数据范围

\(2 \le n \le 1000000\)

分析

考虑枚举在哪一层停下来,算出在当前层停电梯造成的不满意度,然后求个最小的不满意度即可。

前面一步很好想,问题是如何求在当前层停下来,所有人的总不满意度

我们假设当前枚举到了在第 \(k\) 层停电梯

1.对于住在第 \(k\) 层及以上的人:

他们的不满意程度很好求,直接走上去,代价为所有住在楼层 \(k\sim n\) 的人所在层数减去 \(k\) 乘上 \(3\) 的总和

\[3\sum\limits_{i=k}^n(i-k) =3 \sum\limits_{i=0}^{n-k}i = \frac{3(n-k+1)(n-k)}{2} \]


2.对于住在第 \(k\) 层以下楼层的人:

注意到题目说可以有不乘电梯直接从一楼走到家的方案,故我们需要考虑让哪些人不坐电梯才能使代价最小。

首先我们注意到不乘电梯的人与乘电梯的人会有一个明显的分界线,即存在一个特殊的楼层,该楼层即其上面的人通过做电梯再走下来的方案到家,该楼层下面的人直接从一楼走上来。

因为走上来的代价每层为 \(3\) ,但走下来的代价只有\(1\)所以我们尽可能考虑多往下走,但也不是一定往下走都会最优比如我家只住 \(3\) 楼,但电梯在 \(100\)层才会停,与其从 \(100\) 层走下来,不如直接从一楼就走上去。

令这个特殊的楼层为 \(m\)

对于 \(m\) 层以下的人来说,直接走上来是更好的选择,总代价为:

\[3\sum\limits_{i=2}^{m-1}(i-1)=\frac{3m(m-1)}{2} \]

对于 \(m\) 层及以上的人来说,就坐电梯到 \(k\) 层再直接走下来即可,总代价为:

\[\sum\limits_{i=m}^{k}(i-m)=\frac{(k-m)(k-m-1)}{2} \]

两部分相加即为:

\[\frac{3m(m-1) + (k-m)(k-m-1)}{2} \]

我们将它展开得到一个关于 \(m\) 的二次函数:(由于后面与 \(m\) 无关的项与该式在何时取最小值无关,这里用省略号代替)

\[\frac{1}{2}( 4(m-\frac{k+1}{4})^2 + ......) \]

根据我们小学二年级所学的关于二次函数的有关知识,可知当 \(m\)\(\frac{k+1}{4}\) 的时候,该式子最小

\(m\) 的取值就为 \(\frac{k+1}{4}\)

我们不能忽略了这个 \(\frac{k+1}{4}\) 不一定为整数,所以当我们要取这个整数 \(m\) 时,\(m\) 应该是在 \(\frac{k+1}{4}\) 这个数上取整以及下取整中选出一个值,使得其带来的价值最小,分别求取 \(min\) 即可。

时间复杂度是 \(O(n)\)

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

LL get(int m, int k) {
    return (LL)(m - 1) * m * 3 / 2 + (LL)(k - m) * (k - m - 1) / 2;
}
int main() {
	int n; cin >> n;
	LL res = 1e18;
	int pos = 2;
	for (int k = 2; k <= n; k ++ ) {
		LL sum = 0;

		int down = (k + 1) / 4; // 下取整
		int up = (k + 4) / 4; // 上取整
		LL s1 = get(down, k), s2 = get(up, k);

		sum += min(s1, s2);
		sum += (LL)(n - k + 1) * (n - k) / 2 * 3;
		if (sum < res) {
			res = sum;
			pos = k;
		}
	}
	cout << res << ' ' << pos << endl;
	return 0;
}

posted @ 2021-10-29 09:02  NBxiang  阅读(417)  评论(0编辑  收藏  举报