题目链接:https://cometoj.com/contest/58/problem/C?problem_id=2760
这题的代码其实很简洁,主要是有一个坑点。我们一开始可能会想到要去动态规划怎么走,比如哪两个先融合、哪两个先不融合,然后再处理*k区间;或者顺序反过来。这样想的话,整个动态规划的过程就会变得很复杂。
但如果我们把视野放开一些,或者花点时间模拟一下,我们就会发现,所有符石都不*k的情况下,不论如何选择,最终要融合成一个符石的话,总的释放的能量其实是不变的。我们需要做的,其实是判断是否要有*k区间,这个区间从哪里开始、到哪里结束,才能使得最终的能量尽可能小。
这样的话,dp的维度其实就确定了,因为融合的顺序没有影响,我们直接初始数组每次相邻的都融合,O(n)一遍过去,每次都有四种状态,上代码直接理解,注释里继续说。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <string.h>
const int MAX=1e6+5;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
/// https://cometoj.com/contest/58/problem/C?problem_id=2760
ll a[MAX], b[MAX], dp[MAX][5];
int main()
{
int i, j, n, k;
cin >> n >> k;
for (i=0; i<n; i++)
scanf("%lld%lld", &a[i], &b[i]);
for (i=1; i<n; i++)/// 状态数组dp从上向下滚动,对应符石从左到右
{
dp[i][0] = dp[i-1][0] + a[i]*b[i-1];/// 直接与左边符石融合的情况,顺带上左边
///符石和左边的左边符石直接融合产生的能量。dp[i][0]的记录只是为了dp[i][1]的
///选择做铺垫,真正的完全不*k的情况会在dp[i][2]被选择
dp[i][1] = min(dp[i-1][0]+k*a[i]*b[i-1], dp[i-1][1]+k*k*a[i]*b[i-1]);/// 两种
///状态进行选择,一种是左符石没*k,i符石自己乘;一种是i-1符石和i符石一起乘,
///取最优情况记录下来,反正不论选哪个,dp[i][1]记录的都是*k区间覆盖到i符石
///的情况
dp[i][2] = min(dp[i-1][1]+k*a[i]*b[i-1], dp[i-1][2]+a[i]*b[i-1]);/// 也是二选
///一,一种是i-1符石*k了,到i这里断了不乘了;一种是i-1没乘,i也不乘。所以
///dp[i][2]记录的是*k区间没覆盖到i符石的情况
}
cout << min(dp[n-1][1], dp[n-1][2]);/// 这里最终取最优
return 0;
}
其实最终就是有一段区间k、全区间不k这两种结果,而dp记录的是每个状态下释放能量的最优解,所以最终一个max出结果。