原题链接
题目描述
有一堆选手,从左到右枚举每个人,当枚举到 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) (1≤n,m≤2000)
明示时间复杂度 Θ ( 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,j−1−si+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;
}