[BZOJ4897][Thu Summer Camp2016]成绩单

[BZOJ4897][Thu Summer Camp2016]成绩单

试题描述

期末考试结束了,班主任L老师要将成绩单分发到每位同学手中。L老师共有n份成绩单,按照编号从1到n的顺序叠放在桌子上,其中编号为i的成绩单分数为w_i。成绩单是按照批次发放的。发放成绩单时,L老师会从当前的一叠成绩单中抽取连续的一段,让这些同学来领取自己的成绩单。当这批同学领取完毕后,L老师再从剩余的成绩单中抽取连续的一段,供下一批同学领取。经过若干批次的领取后,成绩单将被全部发放到同学手中。然而,分发成绩单是一件令人头痛的事情,一方面要照顾同学们的心理情绪,不能让分数相差太远的同学在同一批领取成绩单;另一方面要考虑时间成本,尽量减少领取成绩单的批次数。对于一个分发成绩单的方案,我们定义其代价为:
其中,k是方案中分发成绩单的批次数,对于第i批分发的成绩单,〖max〗_i是最高分数,〖min〗_i是最低分数。a,b是给定的评估参数。现在,请你帮助L老师找到代价最小的分发成绩单的方案,并将这个最小的代价告诉L老师。当然,分发成绩单的批次数k是由你决定的。

输入

第一行包含一个正整数n,表示成绩单的数量。
第二行包含两个非负整数a,b,表示给定的评估参数。
第三行包含n个正整数w_i,表示第i张成绩单上的分数。

输出

仅一个正整数,表示最小的代价是多少。

输入示例

10
3 1
7 10 9 10 6 7 10 7 1 2

输出示例

15

数据规模及约定

n<=50, a<=100, b<=10, w_i<=300

题解

区间 dp。先将权值离散。设 f(l, r, a, b) 表示对于序列的 [l, r] 段,最终剩下的数的权值都在 [a, b] 范围内所需要的最小代价,特别地,f(l, r, 0, 0) 表示 [l, r] 中都取光的最小代价。转移时我们从 [l, r] 区间往里缩,贪心地将两边权值在 [a, b] 范围内的数丢在一边,假设我们缩到了区间 [tl, tr],那么 f(l, r, a, b) = min{ f(tl, tr, 0, 0), f(tl, k, 0, 0) + f(k+1, tr, a, b), f(tl, k, a, b) + f(k+1, tr, 0, 0), f(tl, k, a, b) + f(k+1, tr, a, b) | k∈[tl, tr) },然后 f(l, r, 0, 0) = min{ f(l, r, a, b) + A + B * (b - a)2 | a ≤ b, a,b∈权值集 }(这里的 A 和 B 分别对应题面中的 a 和 b)。

这样设计状态的思路很像 UVA(算法竞赛入门经典)上的一道题,貌似叫“方块消除”。。。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 55
#define oo 2147483647

int n, A, B, sco[maxn], num[maxn], f[maxn][maxn][maxn][maxn];
void up(int& a, int b) {
	a = min(a, b);
	return ;
}

int main() {
	n = read(); A = read(); B = read();
	for(int i = 1; i <= n; i++) num[i] = sco[i] = read();
	
	sort(num + 1, num + n + 1);
	int m = unique(num + 1, num + n + 1) - num - 1;
	for(int i = 1; i <= n; i++) sco[i] = lower_bound(num + 1, num + m + 1, sco[i]) - num;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			for(int a = 0; a <= m; a++)
				for(int b = 0; b <= m; b++) f[i][j][a][b] = oo;
	for(int len = 1; len <= n; len++)
		for(int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			for(int a = 1; a <= m; a++)
				for(int b = a; b <= m; b++) {
					int tl = l, tr = r;
					while(a <= sco[tl] && sco[tl] <= b) tl++;
					while(a <= sco[tr] && sco[tr] <= b) tr--;
					if(tl > tr) f[l][r][a][b] = 0;
					else if(tl == tr) up(f[l][r][a][b], A);
					else {
						for(int i = tl; i < tr; i++) {
							if(f[tl][i][a][b] < oo && f[i+1][tr][0][0] < oo)
								up(f[l][r][a][b], f[tl][i][a][b] + f[i+1][tr][0][0]);
							if(f[tl][i][0][0] < oo && f[i+1][tr][a][b] < oo)
								up(f[l][r][a][b], f[tl][i][0][0] + f[i+1][tr][a][b]);
							if(f[tl][i][a][b] < oo && f[i+1][tr][a][b] < oo)
								up(f[l][r][a][b], f[tl][i][a][b] + f[i+1][tr][a][b]);
						}
						if(f[tl][tr][0][0] < oo) up(f[l][r][a][b], f[tl][tr][0][0]);
					}
					if(f[l][r][a][b] < oo) up(f[l][r][0][0], f[l][r][a][b] + A + B * (num[b] - num[a]) * (num[b] - num[a]));
				}
		}
	
	printf("%d\n", f[1][n][0][0]);
	
	return 0;
}

 

posted @ 2017-05-20 09:52  xjr01  阅读(633)  评论(0编辑  收藏  举报