2023.7.12 动态规划专题一
T1 [USACO20OPEN] Exercise G
首先我们要承认的一点是 这题我没看懂题干(悲
但是结合样例的话 我们大概可以猜测假如把
并且呢因为显然
那么新的问题就来了 就是这个计数有可能重 比如说我把
那么为了不重 我们钦定只把这个数拆成类似于
那么现在问题就转化为把一个数
答案即为
T2 [USACO20FEB] Delegation G
首先枚举
那么我们思考对于一个指定长度
我们考虑如果这个点上有这样一条链的话 要么它是儿子-它-儿子 要么是儿子-它-父亲(只有一条)
所以我们对每个点进行
具体地 假如我
否则把它也丢进待匹配的集合中
那么最后判待匹配的儿子个数就行了
否则就不合法了
具体可以拿
int dfs(int x, int fa, int len) {
multiset<int> s;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa) continue;
int ans = dfs(y, x, len);
if (ans == -1) return -1; //如果搜到了不合法情况
++ans; //加上儿子和自己连的那条边
ans %= len;
if (ans == 0) continue; //如果正好用完
if (s.count(len - ans)) s.erase(s.find(len - ans)); //寻找能配对的链的出现次数(count) 并删除(find返回len - ans的迭代器)
else s.insert(ans); //否则把它丢进去
}
if (s.size() == 0) return 0; //全配对上了
if (s.size() == 1) return *s.begin(); //注意begin返回的是迭代器
else return -1; //否则不合法
}
T3 Tiles for Bathroom
Konata:对于二维的东西 我们思考一下一维怎么处理
我们考虑这样一件事 假如我知道
那么实际上
所以我们找到恰好为
然后我们发现这题的
那么我们可以枚举右端点 然后维护离它最近的前
进而把这个做法扩展到二维 我们枚举右上角 然后以层为关键字排序后取前
然后往右上角移动并进行转移 这里用一下luogu题解的图
如图 你把黄色线段的颜色丢进去更新即可
T4 Karen and Supermarket
(说句闲话 做的时候就感觉这个妹子真好看 后来搜了一下那场比赛 发现每道题都有这个妹子 给大家贴两张
upd:原来这个妹子叫九条可怜
好了现在说正经的
首先这题优惠券的依赖结构长得就像一棵树 所以就建成一棵树考虑在树上
并且我们发现 对于此题 把商品数量放到状态里 把花费换成
然后就会发现这个的转移非常像树上背包
并且我们注意到优惠券可以使用也可以不使用 所以我们还需要设一个
我们设
那么我们就有
-
(儿子买 个 并且儿子用优惠券) -
(儿子买 个 并且儿子不用优惠券) -
(儿子买 个 并且儿子不可以用优惠券)
初值就是
统计答案把
注意一下
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == fa) continue;
dfs(y, x);
for (int j = siz[x]; j >= 0; --j) { //注意写法 不然复杂度会退化
for (int k = 0; k <= siz[y]; ++k) {
f[x][j + k][1] = min(f[x][j + k][1], f[x][j][1] + f[y][k][1]);
f[x][j + k][1] = min(f[x][j + k][1], f[x][j][1] + f[y][k][0]);
f[x][j + k][0] = min(f[x][j + k][0], f[x][j][0] + f[y][k][0]);
}
}
siz[x] += siz[y];
}
T5 Tree Elimination
为啥我会想不开把这玩意写了
很新的一个做法 实际上序列数就是消除标记的方案数 那么对于一条边 我们可以选择消两边任何一个点嘛
那么我们考虑对于一个点 它如果被消掉了 那就要么被父亲边消了 要么被儿子边消了
考虑转移 假设
所以我们不妨把“被儿子边消掉”细分一下 变成“在父亲边之前被儿子边消掉”和“在父亲边之后被儿子边消掉”
设
那么就有
它被这个儿子删掉 就需要这个儿子被父亲边之后的边删/没被删 之前的儿子都不能把父亲删掉 所以要么被父亲删要么在父亲边之前就被删 后面的不能被父亲边删掉(因为父亲已经没了)
这个转移注意如果这个儿子边在父亲边之前 就转移
这个就是最开始说的那个
哪个儿子都不能把把它删掉 所以要么被父亲删要么在父亲边之前就被删
具体转移方法就见仁见智了 我那个写法是先
T6 Miss Punyverse
首先第一步把点权减一下 判块的权值和
然后我们设
然后我们考虑 因为带根节点的那个连通块有可能要向上合并 所以我们要让此块的大小越大越好 所以我们还要开个
然后就是个树上背包的转移
然后因为带根节点那块(即你
那初值就是
T7 [NOIP2017 提高组] 宝藏
数据范围一眼状压 想到类似哈密顿路的
然后发现因为这题转移的代价和在这棵树里的深度有关 所以我们考虑把深度加进状态里
设
那我们就有
其中
所以我们要对每个状态枚举子集 并确定其转移的最小花费
具体可以参考一下luogu题解代码:
for (int i = 0; i < m; ++i) {
for (int j = i; j != 0; j = (j - 1) & i) { //枚举子集小技巧
bool OK = true; //能转移到
int temp = i ^ j; //这一层要转移的点
for (int k = n - 1; k >= 0; --k) {
if (temp >= (1 << k)) { //k是这层要转移的点
int tmin = 0x3f3f3f3f;
for (int o = 1; o <= n; ++o) {
if (((1 << o - 1) & j) == (1 << o - 1)) tmin = min(tmin, dis[o][k + 1]); //如果o在j里
}
if (tmin == 0x3f3f3f3f) { //转移不到
OK = false;
break;
}
trans[j][i] += tmin;
temp -= (1 << k);
}
}
if (OK == false) trans[j][i] = 0x3f3f3f3f;
}
}
T8 修缮长城 Fixing the Great Wall
首先第一步肯定是排序
然后我们发现一件事 就是已经修复的点一定是一段连续的区间(因为修复点不需要时间 假如说你把那个左端点修了肯定这一段路上的顺路也修了)
所以我们可以设
然后发现这题还需要增加一维时间 但是我们又不能把它加到状态里 不然就炸了
那我们考虑这样做:
把“未来肯定会发生的费用”也加进这个
具体来说就是 因为所有点最后都是要修的嘛 那么假如说你花了
那所有没修的点的修缮费用都会加上
然后考虑转移 假如我们当前的修好区间是
那我们就要算出到达这个点所需的时间 然后再加上这个点的修缮费用还有上面说的那个“未来可定会发生的费用”
所以我们还需要知道目前在哪
进而我们发现 假如
所以我们再开一维
那往左走 我们就有:
这俩取
往右走就同理了
T9 [省选联考 2021 A/B 卷] 滚榜
首先数据范围很状压 那首先我们就有一维状态是当前已经公布分数的集合
然后因为我们要让下一个反超当前的 所以还要记当前的编号
又因为要求所有
然后因为单调不降 还要记给当前这个分配了多少
然后就时间空间双炸了
我们考虑压掉一维 发现有可能对于不同的
那我们就考虑一个最优的
那么显然就要分配
然后借鉴上一道题转移的思路 因为要求
所以我们直接把这些差值提前直接加到那个记录
T10 [NOI Online #3 提高组] 优秀子序列
首先我们考虑把每个数都拆分为一个二进制的集合 那么题意实际就是选择若干个不相交的集合 求它们的并
那么我们设
然后我们枚举加进去的点 判不相交然后转移即可
欧拉函数预处理出来就行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现