题解:P10236 [yLCPC2024] D. 排卡
liangjindong0504 · · 题解
题目传送门
怎么感觉 T4 比 T3 简单啊(因为我不会 T3)。
题意简述
有一个长度为
求分数最大值。
有多测。
思路分析
首先,通过观察
爆搜(你试试看),时间复杂度
然后,求区间值的算法只剩下 dp 了。于是,开始 dp。
俗话说得好,dp 分三步。
-
设置状态(就是你这个 dp 数组表示什么,不然你都不知道在干什么)。
显然,一开始想,
dp_i 设为1 \sim i 的最大值。很明显,这样只能顾及到从右边取的方案,排除。再加一维,很自然想到区间 dp。设置
dp_{i,j} 表示i \sim j 的最大值。但是这样也有问题。即,你虽然能够转移了,但是上一次是在左边还是右边开始的,是个问题,解决不了。终于,状态表示出来了,设
dp_{i,j,0} 为i \sim j 且从左端开始取的最大值,dp_{i,j,1} 即为i \sim j 且从右端开始取的最大值。 -
推转移式(听起来高大上,实际上就是列举出所有可以到达这个状态的状态)
很明显,如果是
dp_{i,j,0} ,那么之前就是dp_{i+1,j,0/1} ,即上一个区间,左右都可以取。枚举即可。而dp_{i,j,1} 同理。当然,还需要加上一个得分。不过,这里就需要用到快速幂了,不然会爆掉。 -
设置初始值(其实就是最基本的状态)
本来,应该设
dp_{i,i,0/1} 的,因为它没法向下转移了。但是由于这里只有一个数,所以答案为0 ,无需初始化。(当然有一些题目的初始值很麻烦,不要小看它)
好了,思路已经推出来了,如果还有不懂,看代码吧(或者私信)。
代码实现
就注意两点:
多测不清空,亲人两行泪!!!
不开 long long 见祖宗!!!
#include<bits/stdc++.h>
using namespace std;
//不开long long见祖宗
#define int long long
int t,n,a[1010],dp[1010][1010][2],modd=998244353;
//快速幂,这里不讲了,可以去看模板,有很多人讲
int fast_pow(int a,int b,int c){
if(a==0&&b==0) return 0;
int x,y,m=1;
y=a,x=b;
while(b){
if(b%2){
m=m*a%c;
}
a=a*a%c;
b/=2;
}
return m;
}
signed main(){
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
//注意,千万要清空
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) dp[i][j][0]=dp[i][j][1]=0;
}
//dp部分
for(int len=1;len<n;len++){
for(int i=1;i+len<=n;i++){
int j=i+len;
//转移。此时第一个是选左边,因此加上的分数应该是a[i]的a[i+1]或a[j]次方(根据情况)
dp[i][j][0]=max(dp[i+1][j][0]+fast_pow(a[i],a[i+1],modd),dp[i+1][j][1]+fast_pow(a[i],a[j],modd));
//第一个选右边同理
dp[i][j][1]=max(dp[i][j-1][0]+fast_pow(a[j],a[i],modd),dp[i][j-1][1]+fast_pow(a[j],a[j-1],modd));
}
}
//比较从左边取和从右边取的较大值
cout<<max(dp[1][n][0],dp[1][n][1])<<endl;
}
return 0;
}