Comet OJ - Contest #8 C题 符文能量 dp动态规划

题目链接: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出结果。

发布了19 篇原创文章 · 获赞 0 · 访问量 501

猜你喜欢

转载自blog.csdn.net/qq_43317133/article/details/99674414