2023“钉耙编程”中国大学生算法设计超级联赛(1)Mr. Liang play Card Game
题目大意
有一款卡牌游戏,游戏规则如下:有 n n n张卡片从左到右排列在一行上。每张卡片都有一个种类和一个等级(初始时,所有卡片的等级都是 1 1 1)。你可以执行以下操作任意次:
- 选择一张卡片并打出。每种卡片类型都有一个值 v i v_i vi,打出一张等级为 k k k的卡片可以获得 P k × v i P^k\times v_i Pk×vi的收益。但卡片等级有一个限制,最大等级为 R R R
- 选择两张相邻且类型和等级都相同的卡片,将它们合并成一张更高等级的卡片
给出卡牌数量 n n n,卡牌种类 m m m, n n n张卡片的排列顺序, v i v_i vi, P P P和 R R R,求最终能获得的最大收益是多少。
有 t t t组数据。
1 ≤ t ≤ 50 , 1 ≤ n ≤ 100 , 1 ≤ m ≤ 20 , 1 ≤ R ≤ 20 , 1 ≤ P ≤ 10 1\leq t\leq 50,1\leq n\leq 100,1\leq m\leq 20,1\leq R\leq 20,1\leq P\leq 10 1≤t≤50,1≤n≤100,1≤m≤20,1≤R≤20,1≤P≤10
数据保证 n n n值超过 20 20 20的数据组数不超过 10 10 10。
题解
设 f l , r , x , k f_{l,r,x,k} fl,r,x,k表示用区间 [ l , r ] [l,r] [l,r]中的卡牌操作后只剩下一张等级为 x x x,种类为 k k k的卡牌所能获得的最大收益, f l , r , 0 , 0 f_{l,r,0,0} fl,r,0,0表示用区间 [ l , r ] [l,r] [l,r]中的卡牌经过操作全部打出所能得到的最大收益。
我们考虑如何转移。
先考虑区间中只剩一张卡牌时的最大收益。如果这张卡牌的等级为 1 1 1,则我们可以枚举卡牌的位置 i i i,求出将 [ l , i − 1 ] [l,i-1] [l,i−1]和 [ i + 1 , r ] [i+1,r] [i+1,r]上的所有卡牌打出之后的所能获得的最大收益。
f l , r , 1 , k = max i = l r { f l , i − 1 , 0 , 0 + f i + 1 , r , 0 , 0 } f_{l,r,1,k}=\max\limits_{i=l}^r\{f_{l,i-1,0,0}+f_{i+1,r,0,0}\} fl,r,1,k=i=lmaxr{fl,i−1,0,0+fi+1,r,0,0}
注意要当 k = a i k=a_i k=ai时才能取 m a x max max。
当等级大于 1 1 1时,我们可以枚举左右区间的断点 i i i,从 [ l , i ] [l,i] [l,i]和 [ i + 1 , r ] [i+1,r] [i+1,r]分别等到种类相同但等级低一级的卡牌,合并得到答案。
f l , r , x , k = max i = l r − 1 { f l , i , x − 1 , k + f i + 1 , r , x − 1 , k } f_{l,r,x,k}=\max\limits_{i=l}^{r-1}\{f_{l,i,x-1,k}+f_{i+1,r,x-1,k}\} fl,r,x,k=i=lmaxr−1{fl,i,x−1,k+fi+1,r,x−1,k}
其中 x > 1 x>1 x>1。
再考虑如何计算 f l , r , 0 , 0 f_{l,r,0,0} fl,r,0,0。先枚举区间 [ l , r ] [l,r] [l,r]可以由哪两个合并得到,这类答案用 w 1 w1 w1来储存。
w 1 = max i = l r − 1 { f l , i , 0 , 0 + f i + 1 , r , 0 , 0 } w1=\max\limits_{i=l}^{r-1}\{f_{l,i,0,0}+f_{i+1,r,0,0}\} w1=i=lmaxr−1{fl,i,0,0+fi+1,r,0,0}
再枚举区间 [ l , r ] [l,r] [l,r]可以剩下什么样的卡牌,将其打出后求出能得到收益,再取最大值,这类答案用 w 2 w2 w2来储存。
w 2 = max x = 1 R max k = 1 m { f l , r , x , k + P x ⋅ v k } w2=\max\limits_{x=1}^R\max\limits_{k=1}^m\{f_{l,r,x,k}+P^x\cdot v_k\} w2=x=1maxRk=1maxm{fl,r,x,k+Px⋅vk}
由此即可得到 f l , r , 0 , 0 f_{l,r,0,0} fl,r,0,0
f l , r , 0 , 0 = max { w 1 , w 2 } f_{l,r,0,0}=\max\{w1,w2\} fl,r,0,0=max{w1,w2}
最后的答案为 f 1 , n , 0 , 0 f_{1,n,0,0} f1,n,0,0。
时间复杂度为 O ( n 3 m R ) O(n^3mR) O(n3mR)。在 1 ≤ n ≤ 100 1\leq n\leq 100 1≤n≤100的情况下,等级的最大值不超过 7 7 7,而且 n n n较大的数据组数较少,所以这是可行的。
code
#include<bits/stdc++.h>
using namespace std;
int t,n,m,R;
long long P,a[105],v[105],mi[25],f[105][105][25][25];
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d%d%lld",&n,&m,&R,&P);
mi[0]=1;
for(int i=1;i<=10;i++) mi[i]=mi[i-1]*P;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%lld",&v[i]);
}
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++){
for(int i=0;i<=R;i++)
for(int j=0;j<=m;j++) f[l][r][i][j]=-1e18;
f[l][r][0][0]=0;
}
for(int i=1;i<=n;i++){
f[i][i][1][a[i]]=0;
f[i][i][0][0]=v[a[i]];
}
for(int len=2;len<=n;len++){
for(int l=1,r=len;r<=n;l++,r++){
for(int w=l;w<=r;w++){
f[l][r][1][a[w]]=max(f[l][r][1][a[w]],f[l][w-1][0][0]+f[w+1][r][0][0]);
}
for(int i=2;i<=R;i++){
for(int j=1;j<=m;j++){
for(int w=l;w<=r-1;w++){
f[l][r][i][j]=max(f[l][r][i][j],f[l][w][i-1][j]+f[w+1][r][i-1][j]);
}
}
}
long long v1=-1e18,v2=-1e18;
for(int i=l;i<=r-1;i++){
v1=max(v1,f[l][i][0][0]+f[i+1][r][0][0]);
}
for(int i=1;i<=R;i++){
for(int j=1;j<=m;j++){
v2=max(v2,f[l][r][i][j]+mi[i-1]*v[j]);
}
}
f[l][r][0][0]=max(v1,v2);
}
}
printf("%lld\n",f[1][n][0][0]);
}
return 0;
}