B.Array Walk
有 n n n个数,从第一个位置开始走,可以选择向左或者向右走,然后获得下一步到达的那个数的值,向左向右走之后的位置不能越界,并且不能连续向左走两次以上,也就是一次向左走之前一定有一次向右走的操作.现在要求恰好经过 k k k次移动并且向左走的次数不超过 z z z,计算经过的数字之和的最大值.有 t t t轮测试,每一轮测试的输入格式是先输入三个整数 n , k , z n,k,z n,k,z,然后输入 n n n个整数,输出进过数字的和的最大值.每个参数的数据范围:
1 ≤ t ≤ 1 0 4 , 2 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ n − 1 , 0 ≤ z ≤ min ( 5 , k ) 1 ≤ a i ≤ 1 0 4 , i = 0 , 1 , … , n ∑ i = 0 k n i ≤ 3 ⋅ 1 0 5 1 \le t \le 10^4,2\leq n \leq 10^5, 1\leq k \leq n-1, 0 \leq z \leq \min(5,k) \\ 1 \leq a_i \leq 10^4, i=0,1,\dots,n\\ \sum_{i=0}^{k}n_i \leq 3\cdot10^5 1≤t≤104,2≤n≤105,1≤k≤n−1,0≤z≤min(5,k)1≤ai≤104,i=0,1,…,ni=0∑kni≤3⋅105
使用dp
,dp[i][j]
这个数组的i
表示移动的次数,j
表示向左移的次数,在第i
次移动时有两个选择向右移动,得到dp[i+1][j
,此时的dp[i+1][j] = max(dp[i+1][j], dp[i][j]+buf[i-2*j+1]);
这里面的i-2*j
表示的是此时的位置,2*j
表示向右移动一次和向左移动一次,buf[i-2*j+1]
表示向右移动后下一个位置的值,这里面有一个约束条件i-2*j >= 0
也就是回退的次数乘以2
不能比此时可以移动的次数i
大.如果此时回退的次数小于z-
并且此时已经移动的次数i
不大于k-2
,那么可以向右移动一次再向左移动,然后移动次数加2
,左移回退次数加1
,得到dp[i+2][j+1]
,对应的语句是dp[i+2][j+1] = max(dp[i+2][j+1], dp[i][j]+buf[i-2*j+1]+buf[i-2*j]);
#include
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int buf[N];
int dp[N][10];
void solve(int n, int k, int z){
memset(dp, 0, sizeof(dp));
dp[0][0] = buf[0];
for(int i = 0; i < k; ++i){
for(int j = 0; j <= z; ++j){
if( i - 2 * j < 0)
break;
// turn right
dp[i+1][j] = max(dp[i+1][j], dp[i][j]+buf[i-2*j+1]);
// turn right and turn left
if( j < z && i + 2 <= k){
dp[i+2][j+1] = max(dp[i+2][j+1], dp[i][j]+buf[i-2*j+1]+buf[i-2*j]);
}
}
}
int ans = 0;
for(int i = 0; i <= z; i++){
ans = max(ans, dp[k][i]);
}
// printf("%lld\n", ans);
cout<<ans<<endl;
}
int main(int argc, char **argv){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
// scanf("%d", &t);
cin>>t;
while(t--){
int n,k,z;
// scanf("%d%d%d", &n, &k, &z);
cin>>n>>k>>z;
z = min(5,z);
for(int i = 0; i < n; i++){
// scanf("%lld", &buf[i]);
cin>>buf[i];
}
solve(n,k,z);
}
}
最初直接用scanf
读的输入数据,然而莫名的竟然TLE
,于是改为cin
并且取消同步,结果就通过了.