题解:B4428 [CSP-X2025 山东] 勇者斗恶龙
这题第一眼 dp,贪心应该很容易被假掉。符合能拆解成子问题、无后效性等等要求。
-
状态设计
读题,发现影响答案的只有两点,分别是第几个人和提升几点。所以设 \(dp_{i, j}\) 表示前 \(i\) 个人中,第 \(i\) 个人提升 \(j\) 点时的最小代价。
要使最终答案最小,所以在两个人冲突的时候能少提升就少提升。所以 \(j\) 只有三点:
- 不提升:不冲突。
- 提升 \(1\) 点,在两个人冲突时选择。
- 提升 \(2\) 点,在提升之后还冲突时的选择。
空间复杂度 \(O(n)\)。
-
转移方程
初始化应该很好想吧。
对于第 \(i\) 个人增加 \(j\) 点时需检查与前一个人增加 \(k\) 点是否冲突,不冲突便转移,否则不转移。
不会排版所以代码放后面了呜呜呜。
-
答案
因为要排 \(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;
}

浙公网安备 33010602011771号