zoukankan      html  css  js  c++  java
  • 2017 ACMICPC亚洲区域赛北京站J题 Pangu and Stones 题解 区间DP

    题目链接:http://www.hihocoder.com/problemset/problem/1636

    题目描述

    在中国古代神话中,盘古是时间第一个人并且开天辟地,它从混沌中醒来并把混沌分为天地。
    刚开始地上是没有山的,只有满地的石头。
    这里有 \(N\) 堆石头,标号为从 \(1\)\(N\) 。盘古想要把它们合成一堆建造一座大山。如果某些堆石头的数量总和是 \(S\) ,盘古需要 \(S\) 秒才能把它们合成一堆,这新的一堆石头的数量就是 \(S\)
    不幸的是,每一次盘古只能把连续的 \(L\)\(R\) 堆石头合并成一堆。
    盘古希望尽快把所有石头合成一堆。
    你能帮帮他吗?如果没有解,则输出 \(0\)

    解题思路

    可以把合并所有石头的过程拆分成几个子步骤,首先合并连续的一些,然后再合并连续的一些,大区间的结果可以由小区间推出,所以就从小区间开始考虑,逐步推向大区间,可以用 dp[i][j][k] 表示区间 [i,j] 分成 k 堆得最小代价,对于固定的一个区间,肯定是取所有情况的最小值,最后答案是 dp[1][n][1] ,注意边界处理,包括刚开始的初始化。

    然后就是代码处理中的一些细节了。

    首先将所有的 f[i][j][k] 置为 INF

    然后对于所有的初始状态(即 f[i][j][j-i+1] )都置为 \(0\)
    因为区间 \([i,j]\) 内本身就有 \(j-i+1\) 个元素,所以这些元素本身就这么多堆,是不需要花费代价去划分的。这就是我所说的初始状态。

    然后就是合并区间了,区间合并一般都是小区间开始合并到大区间,我们这里也不例外(记忆化搜索的话就得反着来了)。
    对于一个区间 \([l,r]\) ,它要么是直接合并成一对,要么是若干个区间拼接到一起。所以我们这里分情况讨论:

    直接合并

    如果区间 \([l,r]\) 直接合并,那么合并成一对的最小代价应该是 \(f[l][r][1]\)
    那么对于区间 \([l,r]\) ,我一定可以将其查分成两个区间 \([l,i]\)\([i+1,r]\) ,其中坐区间有 \(j\) 个元素,右区间有 \(1\) 个元素,并且满足 \(L \le j+1 \le R\)
    于是我们可以从 \(l \sim r-1\) 枚举 \(i\) ,从 \(L-1 \sim R-1\) 枚举 \(j\)
    \(f[l][r][1] = \min(f[l][i][j]+f[i+1][r][1]) + sum[r] - sum[l-1]\) 。(这里 \(sum[r] - sum[l-1]\) 表示区间 \([l,r]\) 范围内的石子数量之和)

    区间拼接

    这里讲的区间拼接其实就是对于两个区间 \([l,j]\)\([j+1,r]\) ,假设他们分别有 \(i-1\) 堆和 \(1\) 堆石子,那么这个区间总共有 \(i\) 堆石子。
    区间拼接就不需要考虑合并了。
    所以对于区间 \([l,r]\) 包含 \(i\) 堆石子的情况,它对应状态 \(f[l][r][i]\) ,那么它总能拆分成两个状态(\(f[l][j][i-1]\)\(f[j+1][r][1]\) ,其中 \(l \le j \lt r\))的拼接。
    可以推导出状态转移方程为: \(f[l][r][i] = min(f[l][j][i-1] + f[j+1][r][1])\) ,其中 \(2 \le i \le len,l \le j \lt r\)

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define INF (1<<29)
    const int maxn = 110;
    int n, L, R, a[maxn], sum[maxn], f[maxn][maxn][maxn];
    int main() {
        while (~scanf("%d%d%d", &n, &L, &R)) {
            for (int i = 1; i <= n; i ++) scanf("%d", a+i);
            for (int i = 1; i <= n; i ++) sum[i] = sum[i-1] + a[i];
            for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) for (int k = 1; k <= n; k ++) f[i][j][k] = INF;
            for (int l = 1; l <= n; l ++) for (int r = l; r <= n; r ++) f[l][r][r-l+1] = 0;
            for (int len = 1; len <= n; len ++) {
                for (int l = 1; l+len-1 <= n; l ++) {
                    int r = l+len-1;
                    for (int i = l; i < r; i ++) {
                        for (int j = L-1; j < R; j ++) {
                            f[l][r][1] = min(f[l][r][1], f[l][i][j] + f[i+1][r][1] + sum[r] - sum[l-1]);
                        }
                    }
                    for (int i = 2; i < len; i ++) {
                        for (int j = l; j < r; j ++) {
                            f[l][r][i] = min(f[l][r][i], f[l][j][i-1] + f[j+1][r][1]);
                        }
                    }
                }
            }
            if (f[1][n][1] == INF) puts("0");
            else printf("%d\n", f[1][n][1]);
        }
        return 0;
    }
    
  • 相关阅读:
    pch文件配置出现 Expected unqualified-id 和 Unkown type name 'NSString'
    App Store Connect Operation Error ERROR ITMS-90032: "Invalid Image Path
    Xcode面板的使用
    KVO的使用
    苹果开发者账号注册-您在注册时提供的地址无效或者不完整
    Apple导出p12证书 导出证书为p12 Apple开发
    iOS开发-导出profile文件
    App Store提交审核报错 ERROR ITMS-90087解决办法
    Win10 的操作中心如果不见了
    什么时候调用dealloc
  • 原文地址:https://www.cnblogs.com/quanjun/p/11953180.html
Copyright © 2011-2022 走看看