题解
⭐:(inf)若需多次加减不宜过大,容易溢出。
dp题呐~
状态:(dp[i][j])表示前(i)轮距离上次(包括当前卡牌)触发双倍已经经过(j)张卡时的最大伤害。
初始值:(dp[i][j]=inf,dp[i][0]=0quad (0le ile n,1le jle 9))
转移方程:设(x1,x2,x3)分别为(c=1)卡牌的最大、次大、第三大(d)值,(y,z)分别为(c=2,3)卡牌的最大(d)值。
[dp[i][j]=dp[i-1][j]\
dp[i][(j+3)\%10]=max(dp[i][(j+3)\%10],dp[i-1][j]+x1*2+x2+x3)quad (jge 7)\
dp[i][j+3]=max(dp[i][j+3],dp[i-1][j]+x1+x2+x3)quad (j<7)\
dp[i][(j+2)\%10]=max(dp[i][(j+2)\%10],dp[i-1][j]+max(x1*2+x2,x1*2+y,y*2+x1))quad (jge 8)\
dp[i][j+2]=max(dp[i][j+2],dp[i-1][j]+max(x1+x2,y+x1))quad (j<8)\
dp[i][0]=max(dp[i][0],dp[i-1][j]+max(x1,max(y,z))*2)quad (j=9)\
dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]+max(x1,y,z))quad (j<9)\
(1le ile n,9ge jge 0)
]
好长,但基本思路就是1行为不选的转移方程,2,4,6行为触发双倍时的方程,其余为普通转移。注意(j)需倒序枚举,否则会从当前回合的状态转移而来。
目标状态:(max_{i=0}^{ile 9} dp[n][i])
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10,inf=0x7fffffff;
struct node {int c,d;} a[N];
int dp[N][15];
signed main()
{
int n,k,ans=0;
scanf("%lld",&n);
//初始化
for(int i=0;i<=n;i++)
for(int j=0;j<=9;j++) dp[i][j]=-inf;
for(int i=0;i<=n;i++) dp[i][0]=0;
//dp
for(int i=1;i<=n;i++)
{
scanf("%lld",&k);
for(int j=1;j<=k;j++) scanf("%lld%lld",&a[j].c,&a[j].d);
//贪心求出x1,x2,x3,y,z
int x1=-inf,x2=-inf,x3=-inf,y=-inf,z=-inf;
for(int j=1;j<=k;j++)
{
if(a[j].c==1)
{
if(a[j].d>x1) x3=x2,x2=x1,x1=a[j].d;
else if(a[j].d>x2) x3=x2,x2=a[j].d;
else if(a[j].d>x3) x3=a[j].d;
}
else if(a[j].c==2) y=max(y,a[j].d);
else z=max(z,a[j].d);
}
//转移
for(int j=9;j>=0;j--) dp[i][j]=dp[i-1][j];
for(int j=9;j>=0;j--)
{
if(j>=7) dp[i][(j+3)%10]=max(dp[i][(j+3)%10],dp[i-1][j]+x1*2+x2+x3);
else dp[i][j+3]=max(dp[i][j+3],dp[i-1][j]+x1+x2+x3);
if(j>=8) dp[i][(j+2)%10]=max(dp[i][(j+2)%10],dp[i-1][j]+max(x1*2+x2,max(x1*2+y,y*2+x1)));
else dp[i][j+2]=max(dp[i][j+2],dp[i-1][j]+max(x1+x2,y+x1));
if(j==9) dp[i][0]=max(dp[i][0],dp[i-1][j]+max(x1,max(y,z))*2);
else dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]+max(x1,max(y,z)));
}
}
//统计答案
for(int i=0;i<=9;i++) ans=max(ans,dp[n][i]);
printf("%lld",ans);
return 0;
}