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