【区间DP】You Are the One HDU - 4283

题目提交点

点我进入官网提交

反思

题目看不懂,以后看题目要把他所说的仔仔细细看个三遍,将重要信息提取出来。
看了网上题解才知道这个题目是什么意思。

题解

  • 题目分析

使用栈来调整原始的出场顺序让不开心值总值最小(原始的出场顺序就是依次入栈的顺序)。

  • 类型分析

栈调整过后的顺序有很多种 + 求最小值,往DP思路上思考。

我们先分析一个例子,加入一个入栈顺序为1 2 3 4 5
如果我们让1第3个出场, 那么有一种可能情况为:3 2 1 4 5,区间 [ 2 , 3 ] [2, 3] [2,3]是在1的前面,区间 [ 4 , 5 ] [4, 5] [4,5]是在1的后面,并且这个区间中的顺序是可以变化的。
通过这个例子我们就可以发现:我们可以将一个区间划分成两个相互独立的区间。正好与区间DP使用条件类似,所以尝试使用区间DP做这个题目。

  • DP分析

我们状态定义为两维, f ( l , r ) f(l, r) f(l,r)集合表示为区间 [ l , r ] [l, r] [l,r]中所有人出栈不开心值的总和。
状态转移方程: f ( l , r ) = m i n ( f ( l , r ) , f ( l + 1 , l + k − 1 ) + f ( l + k , r ) + w i ∗ ( k − 1 ) + k ∗ ( s r − s l + k − 1 ) ) f(l, r) = min(f(l, r), f(l + 1, l + k - 1) + f(l + k, r) + w_i * (k - 1) + k * (s_r - s_{l + k - 1})) f(l,r)=min(f(l,r),f(l+1,l+k1)+f(l+k,r)+wi(k1)+k(srsl+k1))
k k k表示在区间 [ l , r ] [l,r] [l,r]中第k个出栈,这个k是在这个区间中的相对位置,所以的话,如果我们要将子区间组合成总区间的话,还需要加一个偏移量: k ∗ ( s r − s l + k − 1 ) ) k * (s_r - s_{l + k - 1})) k(srsl+k1)), 区间 [ l + 1 , l + k − 1 ] [l + 1, l + k - 1] [l+1,l+k1]是在总区间的前面,所以不用加,而区间 [ l + k , r ] [l + k, r] [l+k,r]是在总区间的后面,所以他们每一个人都会多等k个人: w l + k ∗ k + w l + k + 1 ∗ k + w l + k + 2 ∗ k + w l + k + 3 ∗ k + … … w r ∗ k w_{l + k} * k + w_{l + k + 1} * k + w_{l + k + 2} * k + w_{l + k + 3} * k + …… w_{r} * k wl+kk+wl+k+1k+wl+k+2k+wl+k+3k+wrk

AC 代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 100 + 7, MOD = 1e9, INF = 0x3f3f3f3f;
int f[N][N], w[N], s[N];

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);

    int T;
    cin >> T;
    _for(kase, 1, T){
        int n;
        cin >> n;
        _for(i, 1, n) {
            cin >> w[i];
            s[i] = w[i] + s[i - 1];
        }

        _for(len, 1, n) _for(l, 1, n - len + 1) {
            int r = l + len -1;
            f[l][r] = INF;
            _for(k, 1, r - l + 1)
                f[l][r] = min(f[l][r], f[l + 1][l + k - 1] + f[l + k][r] + w[l] * (k - 1) + k * (s[r] - s[l + k - 1]));
        }

        printf("Case #%d: %d\n", kase, f[1][n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值