牛客多校10 E题
E题感觉这样DP更好理解
枚举两个上界x,y,然后从前往后遍历1~n:
设第i个数为\(a[i]\),当枚举到第i个数时,该数变为目标数所需的操作数为\(t_i - a[i]\)(\(其中t_i可以取x或y\))
可以根据前一个数的操作数来计算当前数所需的操作数,分三种情况:
1.\(t_i - a[i] < 0\)或\(t_{i-1}-a[i-1]<0\),意味着存在某个值大于目标值,此时不合题意
2.\(t - a[i] <= t_{i-1}-a[i-1]\),意味着当前数的操作数<=前一个数的操作数:当前数可以仅依靠前一个数达到目标状态,不需要额外的操作数
3.\(t - a[i] > t_{i-1}-a[i-1]\),意味着当前数的操作数>前一个数的操作数:除了依靠前一个数外,还需要花费额外的操作数来达到目标状态
故可以写出以下函数计算当前所需状态数:
int cmp(int x, int y){
if(x < 0 || y < 0) return MAX;
if(x < y) return 0;
return x - y;
}
设\(dp[i][0]\)表示前i个数的最后一个数变成x的最小操作数,\(dp[i][1]\)表示前i个数的最后一个数变成y的最小操作数。状态转移方程:
dp[k][0] = min(dp[k - 1][0] + cmp(i - a[k], i - a[k - 1]), dp[k - 1][1] + cmp(i - a[k], j - a[k - 1]));
dp[k][1] = min(dp[k - 1][0] + cmp(j - a[k], i - a[k - 1]), dp[k - 1][1] + cmp(j - a[k], j - a[k - 1]));
完整代码:
#include <bits/stdc++.h>
#define MAX 505
using namespace std;
int t;
int n, m;
int a[MAX];
int dp[MAX][2];
int cmp(int x, int y){
if(x < 0 || y < 0) return MAX;
if(x < y) return 0;
return x - y;
}
int main(){
scanf("%d", &t);
while(t--){
int maxn = 0, minn = MAX;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
maxn = max(maxn, a[i]);
minn = min(minn, a[i]);
}
if(m == 1){
int ans = 0;
ans = maxn - a[1];
for(int i = 2; i <= n; i++){
ans += cmp(maxn - a[i], maxn - a[i - 1]);
}
printf("%d\n", ans);
}
else{
int ans = MAX * MAX;
for(int i = maxn; i <= 2 * maxn; i++){
for(int j = minn; j <= 2 * maxn; j++){
dp[1][0] = i - a[1];
dp[1][1] = j - a[1];
for(int k = 2; k <= n; k++){
dp[k][0] = min(dp[k - 1][0] + cmp(i - a[k], i - a[k - 1]), dp[k - 1][1] + cmp(i - a[k], j - a[k - 1]));
dp[k][1] = min(dp[k - 1][0] + cmp(j - a[k], i - a[k - 1]), dp[k - 1][1] + cmp(j - a[k], j - a[k - 1]));
}
ans = min(ans, min(dp[n][0], dp[n][1]));
}
}
printf("%d\n", ans);
}
}
return 0;
}

浙公网安备 33010602011771号