『环形DP』Naptime

题目描述

在这里插入图片描述

题解

如果这是一个线性问题就很简单了。

我们最直观的想法是设 f [ i ] [ j ] f[i][j] 表示到第 i i 个时段,进行了 j j 次休息的最大恢复。

自习发现不值当现在和当前处于什么状态,于是我们设 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] 表示到了第 i i 个时段,进行了j次休息,有( 1 1 )没有( 0 0 )睡觉或准备睡觉的最大恢复值。

很容易推导状态转移方程:
f [ i ] [ j ] [ 0 ] = m a x ( f [ i 1 ] [ j ] [ 0 ] , f [ i ] [ j ] [ 1 ] ) f[i][j][0]=max(f[i-1][j][0],f[i][j][1])
f [ i ] [ j ] [ 1 ] = m a x ( f [ i 1 ] [ j 1 ] [ 0 ] , f [ i 1 ] [ j 1 ] [ 1 ] + U j ) f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+U_j)
初始化就是 f [ 1 ] [ 0 ] [ 0 ] = f [ 1 ] [ 1 ] [ 1 ] = 0 f[1][0][0]=f[1][1][1]=0 .其它为负无穷,表示不合法。

答案: m a x ( f [ n ] [ m ] [ 1 ] , f [ n ] [ m ] [ 0 ] ) max(f[n][m][1],f[n][m][0])

如果思考一下不处理环形对答案带来的影响:会存在忽略n与1交界处存在联系的答案,我们来思考一下这一个联系究竟是什么。

如果 n n 不是睡觉的,那么 n n 不会对 1 1 产生影响,可以在非环形DP中完成转移;如果 n n 是睡觉的,此时如果 1 1 是不睡觉的,那么也可以在非环形DP中考虑到该状态;因此唯一相关的联系就是 n n 作为预备,让 1 1 的睡觉直接产生贡献。

那么我们就可以强制让 n n 睡觉, 1 1 也睡觉产生贡献.

仅仅需要改变初始化和目标状态即可: f [ 1 ] [ 1 ] [ 1 ] = U 1 f[1][1][1]=U_1 ,其余为负无穷。答案: f [ n ] [ m ] [ 1 ] . f[n][m][1].

因此在环形问题中,我们可以考虑一下如何可以用某一些特殊的限制使环形问题转化为线性问题。

代码如下:

#include <bits/stdc++.h> 

using namespace std;
const int N = 4000;

int n, m, ans;
int a[N], f[N][N][2], g[N][N][2];

inline int read(void)
{
    int s = 0, w = 1; char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
    return s * w;
} 

void dp(void)
{
	for (int i=2;i<=n;++i)
	{
		f[i][0][0] = 0;
		for (int j=1;j<=m;++j)
			f[i][j][0] = max(f[i-1][j][0],f[i-1][j][1]),
		    f[i][j][1] = max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]);
	}
	return;
}

int main(void)
{
	freopen("naptime..in","r",stdin);
	freopen("naptime..out","w",stdout);
	n = read(), m = read();
	for (int i=1;i<=n;++i) a[i] = read();
	memset(f,-30,sizeof f);
	f[1][0][0] = 0, f[1][1][1] = 0, dp(), ans = max(f[n][m][0],f[n][m][1]);
	memset(f,-30,sizeof f);
	f[1][1][1] = a[1], dp(), ans = max(f[n][m][1],ans);
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/92640444