加载中...

题解:B4428 [CSP-X2025 山东] 勇者斗恶龙

这题第一眼 dp,贪心应该很容易被假掉。符合能拆解成子问题、无后效性等等要求。

  1. 状态设计

    读题,发现影响答案的只有两点,分别是第几个人和提升几点。所以设 \(dp_{i, j}\) 表示前 \(i\) 个人中,第 \(i\) 个人提升 \(j\) 点时的最小代价。

    要使最终答案最小,所以在两个人冲突的时候能少提升就少提升。所以 \(j\) 只有三点:

    • 不提升:不冲突。
    • 提升 \(1\) 点,在两个人冲突时选择。
    • 提升 \(2\) 点,在提升之后还冲突时的选择。

    空间复杂度 \(O(n)\)

  2. 转移方程

    初始化应该很好想吧。

    对于第 \(i\) 个人增加 \(j\) 点时需检查与前一个人增加 \(k\) 点是否冲突,不冲突便转移,否则不转移。

    不会排版所以代码放后面了呜呜呜。

  3. 答案

    因为要排 \(n\) 个人,对提升次数无限制,所以答案为 \(dp_{n, j}\) 中最大值。

code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<climits>
#include<bitset>
#define ll long long
#define ull unsigned long long
#define endl '\n'
#define y1 Y1
#define pii pair<int,int>
#define mkp make_pair
#define debug(x, y) cout << x << " " << y << endl
using namespace std;

inline int read(){
	int t = 0, flag = 1;
	char a = getchar();
	while(!isdigit(a)){
		if(a == '-')
			flag = -1;
		a = getchar();
	}
	while(isdigit(a))
		t = t * 10 + a - '0', a = getchar();
	return t * flag;
}
//inline void put(int x){
//	if(x < 0) putchar('-'), x = -x;
//	if(x < 10) putchar(x + '0');
//	else put(x / 10), putchar(x % 10 + '0');
//}

const int N = 2e5 + 5;
ll n, a[N], b[N], dp[N][3], ans = 4e18;

signed main(){
	// freopen("hero.in", "r", stdin);
	// freopen("hero.out", "w", stdout);
//	ios::sync_with_stdio(false);
//	cin.tie(0); cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i] >> b[i];
	dp[1][0] = 0, dp[1][1] = b[1], dp[1][2] = 2 * b[1]; //初始化,这个应该比较明显吧。
	for(int i = 2; i <= n; i++){
		for(int j = 0; j <= 2; j++){
			dp[i][j] = 4e18;//设最大值,我好像因为这个调了好久来着。
			for(int k = 0; k <= 2; k++){
				if(a[i - 1] + k != a[i] + j) //判断是否合法,避免出现提升后出现冲突的情况。
					dp[i][j] = min(dp[i][j], dp[i - 1][k] + b[i] * j); 
			} 
		}
	}
	for(int i = 0; i <= 2; i++)
		ans = min(ans, dp[n][i]);//取最后三种方案的最小值。
	cout << ans;
	return 0;
}
posted @ 2025-11-17 18:47  碎碎念的女巫  阅读(6)  评论(0)    收藏  举报