CF #765C Road Optimization

题目
有个   n   \,n\, n路标,长   l   \,l\, l的路,选其中至多   k   \,k\, k个路标,使得走完全程所用时间最少。 ( n < 500 ) (n<500) (n<500)
给出每个路标的位置和限速,到下一个路标前必须以这个速度走,第一个路标不可移除。

思考
显然是个 n 3 n^3 n3 DP,考虑 d p [   i   ] [   j   ] dp[\,i\,][\,j\,] dp[i][j] 表示前 j  ⁣ −  ⁣ 1 j\!-\!1 j1 个路标至多去掉 i i i 个走到 j j j 的最小费用。

为什么不能去掉 j j j 点呢,因为DP要求前面状态不能对后面有影响,所以每个状态必须是确定的, j j j 一定不选,后面从前面状态转移过来就有明确的限速了。
那又为什么说至多去掉 i i i 个呢,去掉 i i i 个不更明确吗,其实DP的重点在于每个DP数组保存的都是当前状态最优情况,重点在于最优,因为最优的时候可能选不够 i i i 个,转移的时候也是从最优的情况转移过去。

考虑一下有啥思路,先想想为什么不能贪心。因为如果贪心,每次选贡献最大的,一种情况,去掉前两个贡献小的,后面一个贡献就会非常大的情况就会错,那么我们容易想出来一个结论,就是每次选的一定是一整段或者一个,这就是从 j  ⁣ −  ⁣ 1 j\!-\!1 j1 个数中选出若干段组成 i i i 个数。

先写出状态转移方程
d p [   i   ] [   j   ] = m i n ( d p [   i   ] [   j   ] ,   d p [   i  ⁣ −  ⁣ p   ] [   j  ⁣ −  ⁣ p  ⁣ −  ⁣ 1   ] + c o s t   ) dp[\,i\,][\,j\,] = min( dp[\,i\,][\,j\,],\ dp[\,i\!-\!p\,][\,j\!-\!p\!-\!1\,] + cost\ ) dp[i][j]=min(dp[i][j], dp[ip][jp1]+cost )

p p p 是枚举的从 j − 1 j-1 j1 位置开始往前数多少个数不选,别看虽然只是枚举了最后这一段,但是已经包含了所有的情况了,因为 d p [   i  ⁣ −  ⁣ p   ] [   j  ⁣ −  ⁣ p  ⁣ −  ⁣ 1   ] dp[\,i\!-\!p\,][\,j\!-\!p\!-\!1\,] dp[ip][jp1] 包含了前面一段选各种段或者不选的最优解。

注意一下,要记得在终点增加一个点,不能被删,也用来计算答案。
代码

#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize (Ofast)
#define fastio ios_base::sync_with_stdio(0); cin.tie(NULL);
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define LL long long
#define mp make_pair
#define pb push_back
#define fr first
#define se second
#define endl "\n"
#define debug1 cout << "???" << endl;
#define debug2(x) cout << #x << ": " << x << endl;
const int INF = 0x3f3f3f3f;
const int N = 5e2+7;
int n, l, k, dp[N][N];
pair <int, int> a[N];
int main()
{
    fastio
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    memset(dp, INF, sizeof(dp));
    cin >> n >> l >> k;
    rep(i, 1, n)
        cin >> a[i].fr;
    rep(i, 1, n)
        cin >> a[i].se;
    a[n+1] = mp(l, 0);
    dp[0][1] = 0;
    rep(i, 2, n+1)
        dp[0][i] = dp[0][i-1] + (a[i].fr - a[i-1].fr) * a[i-1].se;
    rep(i, 1, k)
    {
        rep(j, 2, n+1)
        {
            if(i > j-1)
                continue;
            dp[i][j] = dp[i-1][j];
            rep(p, 0, min(j-1, i))
            {
                int l = j-p-1;
                dp[i][j] = min(dp[i][j], dp[i-p][l] + a[l].se * (a[j].fr - a[l].fr));
            }
        }
    }
    cout << dp[k][n+1] << endl;
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值