2022CCSP T1最少充电次数

这篇具有很好参考价值的文章主要介绍了2022CCSP T1最少充电次数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

记录第一次CCSP竞赛。一共3题,只做出第一题,用时3h30m(累),ac了开心地吃了个午饭。然而饭饱之后,大脑完全提不起神看着题面昏昏欲睡。第二题是虚拟内存,超级大模拟,刚好这个学期学os,但是翘了太多课完全看不懂,自己看ppt学了一点多级页表,但是1v0,1v1啥的想不明白怎么对应呀。第三题跟数据库系统有关,高性能 RDF 图查询系统,给了一个代码框架,稍微看了看,代码十分规范,应用了很多C++继承、虚基类等等特性,然后按要求实现一些函数方法,不会。下面主要记录第一题的思路。


T1 最少充电次数

题面

2022CCSP T1最少充电次数,CSP 认证,算法,动态规划,c++,CCSP竞赛
数据范围
2022CCSP T1最少充电次数,CSP 认证,算法,动态规划,c++,CCSP竞赛

思路

DP,有电量、充电时间两个维度约束,一开始我定义的状态是 d p [ n ] [ r t i m e ] [ r b a t ] dp[n][rtime][rbat] dp[n][rtime][rbat],维度的含义是当前站点、剩余充电时间和剩余电量,存储相应的最小充电次数,但是更新该状态数组会发现剩余电量这一维度是 ( 1 < < 30 ) ≈ 1 e 9 (1<<30)\approx1e9 (1<<30)1e9,这肯定T飞。

其实看到问题很容易产生贪心想法,选择充电效率较高的充电站以在相同的时间内获得更多电量。那其他维度相同的状态中是不是应选择剩余电量更多的状态?想到这点,重新定义状态 d p [ n ] [ r t i m e ] [ a n s ] dp[n][rtime][ans] dp[n][rtime][ans],调换一下,最后一维表示充电次数,数组存储最大剩余电量。
递推分为行驶和充电,由于当前状态仅仅和前面一个状态有关,将第一维度赋为2,滚动数组以压缩空间。
① 行驶至下一个充电站:

dp[s ^ 1][j][k] = max(dp[s ^ 1][j][k], dp[s][j][k] - d[i + 1] + d[i]);

② 充电:

dp[s ^ 1][j][k] = dp[s][j][k];	// t==0
dp[s ^ 1][j - t][k + 1] = max(dp[s ^ 1][j - t][k + 1], dp[s][j][k] + t * cspeed[i]);	// t!=0

优化

仔细算一下复杂度,充电站数×总最大充电时间×充电次数, 512 × 1 e 4 × 512 ≈ 2.5 e 9 512\times1e4\times512\approx2.5e9 512×1e4×5122.5e9,提交上去只能过前面两个点。
然后,开始想办法借助STL进行优化(感觉CCF比赛我总是靠乱搞STL出奇迹)。
用数组存储状态,你只能按下标进行递推,但这会冗余考虑很多不可能的状态,从不可能的状态递推怎么也无法到达可能的状态。于是乎我改用 m a p < n o d e , i n t > s t a t [ 2 ] map<node, int> stat[2] map<node,int>stat[2],其中 node 的定义为

struct node {
    int rtime, cnt;
    bool operator < (const node &d) const {
        return cnt < d.cnt;
    }
};

这个结构仅仅存储有效状态,因而我们也只会从有效状态开始递推,避免冗余。

AC代码

2022CCSP T1最少充电次数,CSP 认证,算法,动态规划,c++,CCSP竞赛
太菜了,一发AC高兴得不得来了。。。

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

int d[550], tlimit[550], cspeed[550];

struct node {
    int rtime, cnt;
    bool operator < (const node &d) const {
        return cnt < d.cnt;
    }
};
// 只对有效状态进行转移
map<node, int> stat[2];

void solve() {
    int totdis, n, maxtime, initbat;
    cin >> totdis >> n >> maxtime >> initbat;
    d[0] = 0;
    for (int i = 1; i <= n; i++) cin >> d[i];
    for (int i = 0; i < n; i++) cin >> tlimit[i];
    for (int i = 0; i < n; i++) cin >> cspeed[i];

    stat[1][{maxtime, 0}] = initbat;

    int s = 1;
    for (int i = 0; i < n; i++) {
        // 从i-1行驶至i
        for (const auto &[x, r] : stat[s]) {
            if (stat[s][x] - d[i + 1] + d[i] >= 0) {
                stat[s ^ 1][x] = stat[s][x] - d[i + 1] + d[i];
            }
        }

        stat[s].clear();
        s ^= 1;

        // 充电
        for (int t = 0; t <= tlimit[i]; t++) {
            // 状态转移
            for (const auto &[x, r] : stat[s]) {
                if (x.rtime < t) continue;
                if (t) {
                    int tmp = 0;
                    if (stat[s ^ 1][{x.rtime - t, x.cnt + 1}]) {
                        tmp = stat[s ^ 1][{x.rtime - t, x.cnt + 1}];
                    }
                    stat[s ^ 1][{x.rtime - t, x.cnt + 1}] = max(tmp, r + t * cspeed[i]);
                }
                else { stat[s ^ 1][{x.rtime, x.cnt}] = r; }
            }
        }

        stat[s].clear();
        s ^= 1;
    }

    // ans
    for (const auto &[x, r] : stat[s]) {
        if (r >= totdis - d[n]) {
            cout << x.cnt << '\n';
            return;
        }
    }
    { cout << "-1\n"; }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

提交代码

仅仅作为个人记录。文章来源地址https://www.toymoban.com/news/detail-717552.html

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
//#define Debug
//#define arr
// 选择充电不一定会充至时间上限
// dp[n][rtime][rbat]:充电次数,(当前所在充电站、剩余充电时间、剩余电量)
// 512*(1e4)*(1<<30)
// 到达终点最少充电次数
/*
 * dp[i][rtime][rbat]=dp[i-1][rtime][rbat+d[i]-d[i-1]]
 * dp[i][rtime-t][rbat+t*cspeed[i]]=dp[i][rtime][rbat]+1,t<=tlimit[i]
 */

// 分组背包
// 每组取物品个数=每个服务站充电时间
// 充电速度不同->选择剩余电量最多的状态
// dp[n][rtime][ans]

/*
 * dp[n][rtime][i]=max(dp[n][rtime+t][i-1]+t*cspeed[i])
 */

int d[550], tlimit[550], cspeed[550];
#ifdef arr
int dp[2][10010][550];   // 该状态下最大剩余电量
#else
struct node {
    int rtime, cnt;
    bool operator < (const node &d) const {
        return cnt < d.cnt;
    }
};
// 只对有效状态进行转移
map<node, int> stat[2];
#endif

void solve() {
    int totdis, n, maxtime, initbat;
    cin >> totdis >> n >> maxtime >> initbat;
    d[0] = 0;
    for (int i = 1; i <= n; i++) cin >> d[i];
    for (int i = 0; i < n; i++) cin >> tlimit[i];
    for (int i = 0; i < n; i++) cin >> cspeed[i];
#ifdef arr
    memset(dp, -1, sizeof dp);
    dp[1][maxtime][0] = initbat;    // 初始化
#else
    stat[1][{maxtime, 0}] = initbat;
#endif

    int s = 1;
    for (int i = 0; i < n; i++) {
        // 从i-1行驶至i
#ifdef arr
        for (int j = maxtime; j >= 0; j--) {
            for (int k = 0; k <= i; k++) {
                dp[s ^ 1][j][k] = max(dp[s ^ 1][j][k], dp[s][j][k] - d[i + 1] + d[i]);
            }
        }
#else
        for (const auto &[x, r] : stat[s]) {
            if (stat[s][x] - d[i + 1] + d[i] >= 0) {
                stat[s ^ 1][x] = stat[s][x] - d[i + 1] + d[i];
            }
        }
#endif

#ifdef Debug
        cout << "arrive: " << i << '\n';
//        for (int j = maxtime; j >= 0; j--) {
//            cout << "rest time: " << j << '\n';
//            for (int k = 0; k <= i && k <= n; k++) {
//                cout << "(" << k << "," << dp[s ^ 1][j][k] << ") ";
//            }
//            cout << '\n';
//        }   cout << '\n';
        for (const auto &x : stat[s ^ 1]) {
            cout << x.rtime << ' ' << x.cnt << ' ' << x.rbat << '\n';
        }   cout << '\n';
#endif
#ifdef arr
        memset(dp[s], -1, sizeof dp[s]);
#else
        stat[s].clear();
#endif
        s ^= 1;
        // 充电
        for (int t = 0; t <= tlimit[i]; t++) {
            // 状态转移
#ifdef arr
            for (int j = maxtime; j >= 0; j--) {
                if (j < t) break;
                for (int k = 0; k <= i && k <= n; k++) {
                    if (!t) {
                        dp[s ^ 1][j][k] = dp[s][j][k];
                    }
                    else {
                        if (dp[s][j][k] < 0) continue;
                        dp[s ^ 1][j - t][k + 1] = max(dp[s ^ 1][j - t][k + 1], dp[s][j][k] + t * cspeed[i]);
                    }
                }
            }
#else
            for (const auto &[x, r] : stat[s]) {
                if (x.rtime < t) continue;
                if (t) {
                    int tmp = 0;
                    if (stat[s ^ 1][{x.rtime - t, x.cnt + 1}]) {
                        tmp = stat[s ^ 1][{x.rtime - t, x.cnt + 1}];
                    }
                    stat[s ^ 1][{x.rtime - t, x.cnt + 1}] = max(tmp, r + t * cspeed[i]);
                }
                else { stat[s ^ 1][{x.rtime, x.cnt}] = r; }
            }
#endif
        }


#ifdef Debug
        cout << "charge: " << i << '\n';
//        for (int j = maxtime; j >= 0; j--) {
//            cout << "rest time: " << j << '\n';
//            for (int k = 0; k <= (i + 1) && k <= n; k++) {
//                cout << "(" << k << "," << dp[s ^ 1][j][k] << ") ";
//            }
//            cout << '\n';
//        }   cout << '\n';
        for (const auto &x : stat[s ^ 1]) {
            cout << x.rtime << ' ' << x.cnt << ' ' << x.rbat << '\n';
        }   cout << '\n';
#endif
//        memset(dp[s], -1, sizeof dp[s]);

#ifdef arr
        memset(dp[s], -1, sizeof dp[s]);
#else
        stat[s].clear();
#endif
        s ^= 1;
    }

    // ans
#ifdef arr
    for (int k = 0; k <= n; k++) {
        for (int j = maxtime; j >= 0; j--) {
            if (dp[s][j][k] >= totdis - d[n]) {
                cout << k << '\n';
                return;
            }
        }
    }
#else
    for (const auto &[x, r] : stat[s]) {
        if (r >= totdis - d[n]) {
            cout << x.cnt << '\n';
            return;
        }
    }
#endif
    { cout << "-1\n"; }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

/*
10 2 2 2
3 8
1 1
2 3

10 2 2 5
3 8
1 1
3 2
 */

到了这里,关于2022CCSP T1最少充电次数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++二分算法:得到子序列的最少操作次数

    二分查找算法合集 给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arr ,arr 可能 包含重复元素。 每一次操作中,你可以在 arr 的任意位置插入任一整数。比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到 [1,4,3,1,2] 。你可以在数组最开始或最后

    2024年02月05日
    浏览(44)
  • 【算法题】2602. 使数组元素全部相等的最少操作次数

    给你一个正整数数组 nums 。 同时给你一个长度为 m 的整数数组 queries 。第 i 个查询中,你需要将 nums 中所有元素变成 queries[i] 。你可以执行以下操作 任意 次: 将数组里一个元素 增大 或者 减小 1 。 请你返回一个长度为 m 的数组 answer ,其中 answer[i]是将 nums 中所有元素变成

    2023年04月24日
    浏览(34)
  • 【动态规划】【记忆化搜索】【回文】1312让字符串成为回文串的最少插入次数

    【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 动态规划汇总 记忆化搜索 回文 字符串 给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。 请你返回让 s 成为回文串的 最少操作次数 。 「回文串」是正读和反读都相同的字符串。

    2024年02月19日
    浏览(47)
  • CCF CSP认证最新2022-12题解c++(全网首发)

    第一次写题解,代码没带注释,请谅解,尽力在题目分析中说明白. http://118.190.20.162/view.page?gpid=T160 问题描述 输入格式 输出格式 输出到标准输出中。 输出一个实数,表示该项目在当前价值标准下的总盈利或亏损。 题目分析 按照题意将除第一年外的每年x元都转换为当前的实际价

    2024年02月13日
    浏览(195)
  • 华为OD机考算法题:根据某条件聚类最少交换次数

    题目部分 解读与思路 代码实现 题目 根据某条件聚类最少交换次数 难度 难 题目说明 给出数字K,请输出所有结果小于K的整数组合到一起的最少交换次数。 组合一起是指满足条件的数字相邻,不要求相邻后在数组中的位置。 数据范围 -100 =K = 100 -100 = 数组中数值 = 100 输入描

    2024年02月09日
    浏览(33)
  • 第27次CCF-CSP计算机软件能力认证(2022-09-18)

    个人感想:算是完成了自己期望的目标300分吧,比之前进步了。第一题花了十五分钟,有十多分钟都是在看题。第二题01背包花了半个小时,太久没看动态规划了模板都忘得差不多。第三题的大模拟依旧有难度,写完的时候离比赛结束还剩一个小时。第四题大概看了一下应该

    2024年02月09日
    浏览(43)
  • 【LeetCode 算法】Minimum Operations to Halve Array Sum 将数组和减半的最少操作次数-Greedy

    给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它 减小 到 恰好 一半 。(注意,在后续操作中你可以对减半过的数继续执行操作) 请你返回将 nums 数组和 至少 减少一半 的 最少 操作数。 1 = n u m s . l e n g t h = 1 0 5 1 = n u m s [ i ] = 1 0 7 1 = num

    2024年02月15日
    浏览(44)
  • 1210. 穿过迷宫的最少移动次数

    你还记得那条风靡全球的贪吃蛇吗? 我们在一个  n*n  的网格上构建了新的迷宫地图,蛇的长度为 2,也就是说它会占去两个单元格。蛇会从左上角( (0, 0)  和  (0, 1) )开始移动。我们用  0  表示空单元格,用 1 表示障碍物。蛇需要移动到迷宫的右下角( (n-1, n-2)  和  (

    2024年02月02日
    浏览(74)
  • 将数组和减半的最少操作次数(力扣)

    给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作) 请你返回将 nums 数组和 至少 减少一半的 最少 操作数。 示例 1: 示例 2: 每次操作,会将数组中的一个数减半。

    2024年02月16日
    浏览(35)
  • 输入单词需要的最少按键次数 I

    输入单词需要的最少按键次数 I 1 = word.length = 26 word 仅由小写英文字母组成 word 中的所有字母互不相同 因为word 中的所有字母互不相同,可以以任意8个字符为一组,第一组每个字符需要按键一次,第二组需要按键两次,以此类推…根据字符串长度将每组字符的按键次数累加起

    2024年01月24日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包