POJ 1701 Dissatisfying Lift (数学推导,枚举)

题目意思:
m层楼,每层i楼有 K[i] 个住客,电梯只能停留在某一层,然后所有的住客,由该楼层走楼梯回家。
加入走楼梯的的层数为N,上楼的不满意度 为 a * N + .05 * N * (N - 1), 下楼的则为
b* N + .05 * N * (N - 1)。题目问,电梯停留在那一层的不满意度总和最小。

参考网上老哥的公式推导 https://ishare.iask.sina.com.cn/f/avsXq1tzeSc.html
本题要点:
1、d[p] 表示电梯停留在p层的不满意度
比较 d[p + 1] 和 d[p] 的差值,分解为三部分
第一部分:s1 = sum{1 * K[1], 2 * K[2], …, n * K[n]}, 这部分是常数值
第二部分:用数组 s2[MaxN] 存储,假设电梯停留在p层
s2[p] = (b + p) * sum{K[1], K[2], … , K[p]}
第三部分:用数组 s3[MaxN] 存储, 假设电梯停留在p层
s3[p] = (a - p - 1) * sum{K[p + 1], K[p + 1], … , K[n]}

d[p + 1] - d[p] = -s1 + s2[p] - s3[p]
d[p + 1] = d[p] + -s1 + s2[p] - s3[p]	//递推公式

2、先计算 s1, 复杂度 O(n);
同理数组 s2, s3, 复杂度也是 O(n);
先计算 电梯停留在一层的 d[1], 利用递推公式可以计算每一层 的 不满意度d[p],
记录最小值即可

#include 
#include 
#include 
#include 
using namespace std;
const int MaxN = 10010;
int Test, n, a, b;
long long K[MaxN];
long long s1, k_sum;
long long sumi[MaxN];
long long s2[MaxN];
long long s3[MaxN];

void init()
{
	s1 = 0;
	for(int i = 1; i <= n; ++i)
	{
		s1 += K[i] * i;	
	}
	memset(s2, 0, sizeof(s2));
	memset(s3, 0, sizeof(s3));
	for(int i = 1; i <= n; ++i)
	{
		s2[i] = (b + i) * sumi[i];
		s3[i] = (a - i - 1) * (k_sum - sumi[i]);
	}
}

void solve()
{
	long long d = 0, ans = -1;
	//电梯停留在1层, 计算不满意值 d
	for(int i = 2; i <= n; ++i)
	{
		d += a * (i - 1) * K[i];
		d += K[i] * (i - 1) * (i - 2) / 2; 
	}
//	printf("d = %lld\n", d);
	ans = d;
	int floor = 1;
	for(int i = 2; i <= n; ++i)
	{
		d = d - s1 + s2[i - 1] - s3[i - 1];
		if(d < ans)
		{
			ans = d;
			floor = i;
		}
	}
	printf("%d\n", floor);
}

int main()
{
	scanf("%d", &Test);
	while(Test--)
	{
		k_sum = 0;
		memset(sumi, 0, sizeof(sumi));
		scanf("%d%d%d", &n, &a, &b);
		for(int i = 1; i <= n; ++i)
		{
			scanf("%lld", &K[i]);		
			k_sum += K[i];
			sumi[i] = K[i] + sumi[i - 1];
//			printf("sumi[%d] = %lld\n", i, sumi[i]);
		}
		init();
		solve();
	}
	return 0;
}

/*
1
5 3 2
1 1 1 1 1
*/

/*
3
*/

你可能感兴趣的:(POJ,数学推导,枚举)