Educational Codeforces Round 97 (Rated for Div. 2) C. Chef Monocarp(DP)

传送门


题目大意

给出一个序列,现在的时间是从 1 1 1开始计时的,每次增加一分钟,对于一个固定的时间 t t t能任意选择序列的一个数 a i a_i ai,此时对答案的贡献为 ∣ t − a i ∣ |t-a_i| tai,每个数只能且必须选一次,问最小的贡献和是多少。

解题思路

一开始以为是贪心,但是写的代码有点难调,后来就干脆放弃了,好像题解里面没有写贪心的。实际上对于数据来说,完全支持 O ( n 2 ) O(n^2) O(n2)的复杂度。而且数据范围比较小,一般来说这样的题目要么是思维模拟要么就是DP了。

首先对序列排序是肯定的,设 d [ i ] [ j ] d[i][j] d[i][j]为对于第 i i i个数在时间 j j j之前得到的最优解,对于本题来说是如何使得时间的分配不会重复。实际上对于当前的时间 t t t,如果选择了当前的数,那么对下一个数产生的影响是只能选择大于 t t t的时间,于是启发我们使用刷表法,那么状态转移方程为:

d [ i + 1 ] [ j + 1 ] = m i n ( d [ i + 1 ] [ j + 1 ] , d [ i ] [ j ] + a b s ( j − a [ i ] ) ) d[i+1][j+1]= min(d[i+1][j+1],d[i][j]+abs(j-a[i])) d[i+1][j+1]=min(d[i+1][j+1],d[i][j]+abs(ja[i]))

上述状态转移方程给定是选择该时间,如果不选择该时间,怎么得到后面时间最优还是前面的选择更优?类似于 01 01 01背包,只需要不断将当前数得到的最优解随着时间增加而向后更新:

d [ i ] [ j + 1 ] = m i n ( d [ i ] [ j + 1 ] , d [ i ] [ j ] ) d[i][j+1]=min(d[i][j+1],d[i][j]) d[i][j+1]=min(d[i][j+1],d[i][j])

然后就是设想所有的数都是 200 200 200,那么显然时间最多可以选到 400 400 400,因此时间 j j j更新到 2 ∗ n 2*n 2n或者直接设置为 400 400 400

初始化和边界

要将dp数组初始化为正无穷,而 d [ 1 ] [ 1 ] = 0 d[1][1]=0 d[1][1]=0,很像背包问题的边界设置。

答案

可以直接在DP过程更新答案到 d [ n + 1 ] [ 2 n ] d[n+1][2n] d[n+1][2n],也可以最后遍历所有的 d [ n + 1 ] [ j ] d[n+1][j] d[n+1][j]取最小值。


//
// Created by Happig on 2020/11/4
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int a[205], d[205][405];

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n;
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        sort(a + 1, a + 1 + n);
        memset(d, 0x3f, sizeof d);
        d[1][1] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= 400; j++)
                if (d[i][j] < inf) {
                    d[i + 1][j + 1] = min(d[i + 1][j + 1], d[i][j] + abs(a[i] - j));   //选择当前时间得到的最小值
                    d[i][j + 1] = min(d[i][j + 1], d[i][j]);     //将取第i个物品的最优解向后传递
                }
        }
        int ans = inf;
        for (int j = 1; j <= 401; j++) {
            ans = min(ans, d[n + 1][j]);
        }
        cout << ans << ENDL;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值