Codeforces1322D Reality Show

原题链接

洛谷的
CF的
VJ的
只能随便看看的(千万别认真了!)

题目描述

有一堆选手,从左到右枚举每个人,当枚举到 i i i时可以让他上场表演获得 c l i c_{l_i} cli的表演费,花费 s i s_i si。此时,如果场上有两个人 i i i j j j,使得 l i = = l j l_i==l_j li==lj,那么他俩会打一架,一个人被打死,另一个人晋一级并再表演一次,收益自然也是 c l i + 1 c_{l_i+1} cli+1,没有花费。要求选出来的选手 l i l_i li不上升。
好像又啰嗦了

解题思路

先康康数据范围:

The first line contains two integers n n n and m m m ( 1 ≤ n , m ≤ 2000 ) ( 1 \le n, m \le 2000) (1n,m2000)

明示时间复杂度 Θ ( n ( m + n ) ) \Theta(n(m+n)) Θ(n(m+n))啊( m + n m+n m+n的原因是因为本题晋级之后最多 m + n m+n m+n个等级)。好的,这种复杂度一般都是 d p dp dp,那么就努力往 d p dp dp上思考。说实话这样就考虑到dp还是有亿点玄学的。 现在来构造 d p dp dp,执行以下五步:
第一步:分析状态所要装的值。因为本题求的是雇佣选手得到的最大收益,所以 f f f数组要装最大值。
第二步:设计状态。这个题要的是选手不上升,那么可以设计状态 f i , j f_{i,j} fi,j表示使用等级 i . . . n i...n i...n的选手,第 i i i个等级的选 j j j个。因为这样在后面不方便计算,所以反着计算序列,把状态就可以改成用 1... i 1...i 1...i的最大值。
第三步:状态转移方程设计。首先,可以有一种直接选择当前选手的策略,则 f i , j = f i , j − 1 − s i + c l i f_{i,j}=f_{i,j-1}-s_i+c_{l_i} fi,j=fi,j1si+cli,其中 j j j枚举的是当前等级的选手选的个数。其次,还可以选了这个选手晋级,则 f j + 1 , k ÷ 2 = f j , k + k ÷ 2 × c j + 1 f_{j+1,k\div2}=f_{j,k}+k\div2\times c_{j+1} fj+1,k÷2=fj,k+k÷2×cj+1,其中 j j j枚举的是升级之前的等级, k k k枚举的是升级之前使用的个数。
第四步:边界情况。最开始一个都没有选,所以只有每种等级的选手一个都不选是 0 0 0 f i , 0 = 0 f_{i,0}=0 fi,0=0,否则设为极小值。
第五步:考虑答案状态。不难想到晋级到 m + 1 m+1 m+1是显然不可能的,但是其实晋级到 m m m就不可能了。则输出 f m , 0 f_{m,0} fm,0即可。

丑陋代码

#include<bits/stdc++.h>
using namespace std;
const int NN=2004;
int l[NN],s[NN],c[NN*2],f[NN*2][NN];
int main()
{
    
    
	int n,m;
	scanf("%d%d",&n,&m);
	m=n+m;
	for(int i=n;i;i--)
		scanf("%d",&l[i]);
	for(int i=n;i;i--)
		scanf("%d",&s[i]);
	for(int i=1;i<=m;i++)
		scanf("%d",&c[i]);
	memset(f,0xaf,sizeof(f));
	for(int i=0;i<=m;i++)
		f[i][0]=0;
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=n;j;j--)
			f[l[i]][j]=max(f[l[i]][j],f[l[i]][j-1]+c[l[i]]-s[i]);
		for(int j=l[i];j<=m;j++)
			for(int k=0;k<=n>>j-l[i];k++)
				f[j+1][k/2]=max(f[j+1][k/2],f[j][k]+k/2*c[j+1]);
	}
	printf("%d",f[m][0]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44043668/article/details/109137156