牛客多校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;
}
posted @ 2025-08-16 13:09  LIGHTB  阅读(11)  评论(0)    收藏  举报