2022CCSP T1最少充电次数

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


T1 最少充电次数

题面

2022CCSP T1最少充电次数_第1张图片
数据范围
2022CCSP T1最少充电次数_第2张图片

思路

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 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最少充电次数_第3张图片
太菜了,一发AC高兴得不得来了。。。

#include 
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;
}

提交代码

仅仅作为个人记录。

#include 
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
 */

你可能感兴趣的:(CSP,认证,ACM,算法,动态规划,c++,CCSP竞赛)