【题解】Acting Cute

题目

测试地址:POJ2228USACO 2005 January Gold

题目来源:USACO 2005 January Gold / 模拟赛 T2(本题题面)。

题目描述

正在 rainbow 的城堡游玩的 Freda 恰好看见了在地毯上跳舞卖萌的水叮当……于是……

Freda:“呜咕>_< 我也要卖萌T_T!”

rainbow 给了 Freda \(N\) 秒的自由活动时间,不过由于刚刚游览城堡有些累了,Freda 只想花 \(B\) 秒的时间来卖萌,剩下的时间她要在 rainbow 的城堡里睡个好觉好好休息一下。

rainbow 给这 \(N\) 秒每秒定义了一个值 \(U_i\),如果第 \(i\) 秒钟 Freda 在卖萌,那么她可以获得 \(U_i\) 点卖萌指数lala~

Freda 开始卖萌后可以随时停止,休息一会儿之后再开始。不过每次 Freda 开始卖萌时,都需要 \(1\) 秒来准备= =,这一秒是不能获得卖萌指数的。当然,Freda 卖萌和准备的总时间 不能超过 \(B\)

更特殊的是,这 \(N\) 秒钟时间是 环形 的。也就是 Freda 可以从任意时间开始她的自由活动并持续 \(N\) 秒。

为了使自己表现得比水叮当更萌,现在 Freda 想知道,她最多能获得多少卖萌指数呢?

输入格式

第一行包含两个整数 \(N\)\(B\)

\(2\)~\(N+1\) 行每行一个整数,其中第 \(i+1\) 行的整数表示 \(U_i\)

输出格式

输出一个整数,表示 Freda 可以获得的最大卖萌指数。

数据范围

各个测试点时间限制 \(1\mathrm{s}\),空间限制 \(1\mathrm{GiB}\)

  • 对于 \(60\%\) 的数据,\(N\leq 100\)
  • 对于 \(100\%\) 的数据,\(0\leq B\leq N\leq 3600\)\(0\leq U_i\leq 200000\)

分析

这道题可以看出来是 \(\mathtt{DP}\),只不过环形这个条件比较难搞。

线性情况

状态定义和递推

先考虑线性的情况,定义 \(f_{i,j}\) 为前 \(i\) 分钟已经卖萌 \(j\) 分钟的卖萌指数最大值。

则可以得到:(\(val(i,j)\)代表从 \(i\) 时刻到 \(j\) 时刻的卖萌指数)

\[\huge{f_{i,j} = \max\{f_{i-1,j}, \max_{0< k\leq j}\{f_{i-k,j-k}+val(i-k+1,i)\}\}} \]

在这个基础上,我们发现这个算法的复杂度是 \(\Theta(n^3)\) 的,只能通过部分分。如何优化?

优化

首先,\(val(i,j)\) 是可以通过前缀和实现 \(\Theta(1)\) 的(设前缀和数组为\(\{pref_i\}\))。接下来考虑 \(\max_{0< k\leq j}\{f_{i-k,j-k}\}\)

考虑数列 \(l_{k,u} = \max_{k\leq t\leq u}\{f_{t,t-k}-pref_{t+1}\}\),则 \(\max_{0< k\leq j}\{f_{i-k,j-k}\}\) 就是 \(l_{i-j,i-1}+pref_i\)

每次计算出 \(f_{i,j}\) 后,可以 \(\Theta(1)\) 更新 \(l_{i-j,i}\),这样就可以将复杂度降至 \(\Theta(n^2)\)

同时 \(l_{k,u}\) 可以简化成 \(l_k\),节省空间。(虽然这道题有的是空间)

上代码:

for (int i = 1; i <= n; i++)
    for (int j = 0; j <= i && j <= lim; j++)
    {
        dp[i][j] = ab_max(dp[i-1][j], ln[i-j] + pref[i]);
        if (i != n)
            ln[i-j] = ab_max(ln[i-j], dp[i-1][j] - pref[i+1]);
    }

环形情况

既然线性情况 Get,那么环形又如何处理呢?

对于最优情况,首先考虑这几个结论:

  • 卖萌结束后的下一分钟,卖萌必然不开始。

为什么?如果下一分钟开始了,这一分钟的卖萌指数就无法算入。但是如果将这一分钟连上上一分钟的卖萌,这一分钟的卖萌指数就能算入。

考虑到 \(U_i \geq 0\),所以算上一定 不差于 不算上。

  • 线性与环形的唯一差别就是第 \(1\) 分钟有没有卖萌 (不是准备)

为什么?因为如果第 \(1\) 分钟没有卖萌或在做准备,则与线性的某种情况等价。

唯一要考虑的环形情况就是有跨越的卖萌时间段。

所以,我们再进行一遍 \(\mathtt{DP}\)。只不过这一次 \(\mathtt{DP}\) 强制要求第 \(1\) 分钟正在卖萌,而且从某一时刻到第 \(n\) 分钟一直在卖萌(有准备)。

为了思路清晰,这次使用 \(revdp_{i,j}\) 进行记录。

上代码:

memset(ln, 0xcf, sizeof(ln));
revdp[0][0] = revdp[1][0] = 0;
ln[0] = -pref[1]; // 注意初始化

revdp[0][0] = 0;
for (int i = 1; i <= lim; i++)
	revdp[i][i] = pref[i];

for (int i = 1; i < n; i++)
	for (int j = 0; j < i && j <= lim; j++)
	{
		revdp[i][j] = ab_max(revdp[i-1][j], ln[i-j] + pref[i]);
		ln[i-j] = ab_max(ln[i-j], revdp[i-1][j] - pref[i+1]);
	}

for (int i = 0; i <= lim; i++)
	revdp[n][i] = ln[n-i] + pref[n];

最后只需要在 \(dp_{n,b}\)\(revdp_{n,b}\) 中选择一个最小值就行了。

Code

#include <cstdio>
#include <cstring>
using namespace std;

const int max_n = 3600;

int pref[max_n+1], dp[max_n+1][max_n+1], ln[max_n+1], revdp[max_n+1][max_n+1] = {};

inline int ab_max(int x, int y) { return (x > y)? x:y; }

int main()
{
	memset(dp, 0xcf, sizeof(dp));
	memset(revdp, 0xcf, sizeof(revdp));
	memset(ln, 0xcf, sizeof(ln));
	dp[0][0] = dp[1][0] = 0;
	
	int n, lim, tmp;
	
	scanf("%d%d", &n, &lim);
	
	if (lim <= 1)
	{
		puts("0");
		return 0;
	}
	
	pref[0] = 0;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &tmp);
		pref[i+1] = pref[i] + tmp;
	}
	ln[0] = -pref[1];
	
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= i && j <= lim; j++)
		{
			dp[i][j] = ab_max(dp[i-1][j], ln[i-j] + pref[i]);
			if (i != n)
				ln[i-j] = ab_max(ln[i-j], dp[i-1][j] - pref[i+1]);
		}
	
	memset(ln, 0xcf, sizeof(ln));
	revdp[0][0] = revdp[1][0] = 0;
	ln[0] = -pref[1];
	
	revdp[0][0] = 0;
	for (int i = 1; i <= lim; i++)
		revdp[i][i] = pref[i];
	
	for (int i = 1; i < n; i++)
		for (int j = 0; j < i && j <= lim; j++)
		{
			revdp[i][j] = ab_max(revdp[i-1][j], ln[i-j] + pref[i]);
			ln[i-j] = ab_max(ln[i-j], revdp[i-1][j] - pref[i+1]);
		}
	
	for (int i = 0; i <= lim; i++)
		revdp[n][i] = ln[n-i] + pref[n];
	
	printf("%d\n", ab_max(dp[n][lim], revdp[n][lim]));
	
	return 0;
}

备注

标算使用的定义是 \(f_{i,j,k}\) 为前 \(i\) 分钟共卖萌 \(j\) 分钟,第 \(i\) 分钟的状态为 \(k\) 时的最大卖萌指数。

则状态转移就是 \(f_{i,j,k}=\begin{cases}\max_{u=0,1}\{f_{i-1,j,u}\}&k=0\\\max\{f_{i-1,j-1,0},f_{i-1,j-1,1}+U_i\}\}&k=1\end{cases}\)

至于具体的细节,供读者思考。(明明是 5ab 太菜了,想不出来)

posted @ 2020-02-16 11:06  5ab  阅读(121)  评论(0编辑  收藏  举报