我们可以只考虑下列关键状态
首次到达
情况 1:
情况 2:
有一个
有一只小熊想从图的左上角走到右下角。每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能出界。
小熊会取走经过的方格中的整数,求它能取到的整数之和的最大值。
从
从第
把从
分两步计算一列格子的 DP 值:
从上到下
从下到上
有
如果可以任意安排摆渡车的出发时间,那么这些人的等车时间之和最少是多少?
一个人的等待时间 = 他乘的车的出发时刻 - 他到车站的时刻
一个最优解应该具有下述性质
解释:如果摆渡车等了几分钟才出发,但是在等待期间没有新的学生来,那不如不等。如果等待了
我们还可以给最优解加一个限制
解释:空车跑和等
不妨设
考虑 DP。
摆渡车在状态
每次到站就立即出发,直到把所有人都送到学校。
连续运行几次之后,下一次等到时刻
为了使第一种情况也对应到一个状态,我们补充一个
int main() {
int n, m; cin >> n >> m;
vector<int> t(n);
for (int i = 0; i < n; i++) cin >> t[i];
sort(t.begin(), t.end());
t.push_back(t.back() + 2 * m);
vector<int> sum(n + 1);
for (int i = 0; i < n; i++) sum[i + 1] = t[i] + sum[i];
vector<int> dp(n + 1, INT_MAX);
for (int i = 0; i < n; i++) {
dp[i] = min(dp[i], i * t[i] - sum[i]); // 摆渡车第一次出发是在 t[i] 时刻
int wait = 0; //乘坐连续运行的车的那些人的等待时间之和
for (int j = i + 1, ptr = i + 1; j <= n; j++) { // 双指针
if (t[j] >= t[i] + m) {//在t[i]时刻出发,连续运行若干次,下一次在 t[j] 时刻出发。
int r = (t[j] - t[i]) % m;
int last = t[j] - r - m; // 上一趟车的出发时刻
while (t[ptr] <= last) {
int s = (t[ptr] - t[i]) % m;
if (s)
wait += m - s;
ptr++;
}
int wait_2 = t[j] * (j - ptr) - (sum[j] - sum[ptr]);
dp[j] = min(dp[j], dp[i] + wait + wait_2);
}
}
}
cout << dp[n] << '\n';
}
一个游戏有若干轮,每一轮规则如下:
处理
接龙游戏是一个典型的多阶段决策过程。
如何找到恰当的子问题?
每一次决策需要哪些信息?
如何应对「这一轮接龙的人不能与上一轮相同」这个限制?
若能,最后一轮上场的人是否唯一?
若唯一,是谁?
如果我们能算出全部
下一步怎么办?
思路一:对每个
思路二:对每个
思路三:对每个人
如果第
那么考虑从状态
如果存在一个
那么状态
对于
有
给你一个长为
你要将每个球染成红色或蓝色,然后按如下方式计算每个球
你的最终得分为
你需要解决
一个球是什么颜色不重要,重要的是两个球的颜色是否相同。
考虑 DP。
边界条件:
对于
球
此时前
球
设球
再细分为两种情况

边界条件:
球
那么,球
于是前
我们要计算
为此我们维护序列
随着
(比朴素的方法)更快地计算 DP。
有
我们要从一共
对每个
在本题的限制条件下,对每个
时间:
用前面提到的维护滑动窗口最大值的套路,能够把时间降到
但是这还不够。
能不能更快?
问题在于有
具体地说就是
这已经有
就算用滚动数组的技巧,也只是把
我们还有一条路可走:重新定义子问题。
这样做有什么好处?
从第
因此只考虑满足
状态数被削减到
至于转移,仍可以用滑动窗口最大值的套路,在均摊
有
高桥要选一些物品放进一个容量是
高桥想要选一些物品放进背包使得所有种类的总满意度最大而总重量不超过
最多能选
令
整理成
把
对
求一些一次函数(直线)在某一点处的函数值的最大值。
例子:
考虑两个一次函数
存在
求
求
考虑按
假设我们维护了一列对求值点
那么
现在直线
设
struct convex_hull_trick { // 每次加入的直线的斜率严格递增
struct line {
long long k, b;
};
vector<line> lines; // 直线序列
vector<long long> r; // 交点序列
void add(long long k, long long b) {
while (!lines.empty()) {
// 计算直线 (k, b) 和直线 lines.back() 的交点
auto [k2, b2] = lines.back();
assert(k2 < k);
// k * x + b >= k2 * x + b2 ==> x >= (b2 - b) / (k - k2)
long long x = (b2 - b) / (k - k2);
if (x * k + b < x * k2 + b2)
x++;
if (r.size() && x <= r.back()) {
r.pop_back();
lines.pop_back();
} else {
r.push_back(x);
break;
}
}
lines.push_back({k, b});
}
long long query(long long x) {
assert(!lines.empty());
auto pos = upper_bound(r.begin(), r.end(), x) - r.begin();
return x * lines[pos].k + lines[pos].b;
}
};
# [abc373_f](https://atcoder.jp/contests/abc373/tasks/abc373_f) Knapsack with Diminishing Values