链接: B. Array Walk
题意:
给一个数列 ,初始价值为 a[1],每次你可以选择向右走或者向左走,走完获得当前位置的数值,最多向左走 z 次,并且不能连续两次向左走,问走 k 步获得的最大价值。
思路:
dp [ i ][ j ] [ k ] 表示当前走第 i 步,已经向左走了 j 次,当前向左还是向右。然后直接转移就好了,如果当前向右,那上一步既可以向左也可以向右,如果当前向左,那上一步只能向右。还有当前位置为 i - 2 * j + 1.
(1表示向左,0表示向右)
dp[i][j][0] = max(dp[i-1][j][0] ,dp[i-1][j][1]) + a[i - 2 *j +1];
dp[i][j][1] = dp[i-1][j-1][0] + a[i - 2 * j + 1];
代码:
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
int T,z;
ll n,a[maxn],k,dp[maxn][10][3],ans;
void init(){
for(int i =0;i<n;i++){
for(int j=0;j<6;j++){
dp[i][j][0] = dp[i][j][1] = 0;
}
}
ans = 0;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld%lld%d",&n,&k,&z);
init();
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
dp[0][0][0] = a[1];
dp[0][0][1] = a[1];
for(int i = 1; i <= k; i ++){
for(int j = 0; j <= z; j ++){
if(i - 2 * j + 1 <= 0) continue;
dp[i][j][0] = max(dp[i-1][j][0] ,dp[i-1][j][1]) + a[i - 2 *j +1];
dp[i][j][1] = dp[i-1][j-1][0] + a[i - 2 * j + 1];
}
}
for(int i = 0;i <= z;i ++){
ans=max(ans,dp[k][i][0]);
ans=max(ans,dp[k][i][1]);
}
printf ("%lld\n",ans);
}
}
或者记忆化搜索一下,也可以,比 dp 慢一点
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
int T,z;
ll n,a[maxn],k;
ll dp[maxn][10][3];
void init(){
for(int i =0;i<n;i++){
for(int j=0;j<6;j++){
dp[i][j][0] = dp[i][j][1] = -1;
}
}
}
ll dfs(int v,int pos ,int x,int sta){
ll ans=-1;
if(dp[v][x][sta] != -1) return dp[v][x][sta];
if(v == k) return a[pos];
if(pos == 1) ans = max(ans, dfs(v+1,pos+1,x,1) + a[pos]);
else if(pos == n) ans = max(ans, dfs(v+1,pos-1,x-1,0) +a[pos]);
else if(x == 0) ans= max(ans, dfs(v+1,pos+1,x,1) + a[pos] );
else if(sta == 0) ans = max(ans, dfs(v+1,pos+1,x,1) + a[pos] );
else ans = max(ans, max(dfs(v+1,pos+1,x,1) + a[pos], dfs(v+1,pos-1,x-1,0) +a[pos]));
dp[v][x][sta] = ans;
return ans;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld%lld%d",&n,&k,&z);
init();
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
printf ("%lld\n",dfs(0,1,z,0));
}
}