TG可能会用到的动态规划-简易自学
完整校订版见此
以下为未核对不完整版本。
因版权原因,完整精校版不向所有公众开放。
请从您找到本博客的地址查找附带密码(比如简书分享了本网址,请您从简书分享页底部查询密码),感谢您的配合。
这里Pleiades_Antares小可爱qiumi
这几天突然回忆起来以前上的一个课qiumi
然后把当时上课做的笔记简单整理了一下发上来(主要是为了给自己复习orz)
于是就有了这个自学教程(基本上看着这个博客是完全可以学会1551)
希望能够帮助到正在拼命复习NOIP的你辣
没有学过动态规划的戳这个超级简单易懂的动态规划教程!!包教包会!
TG考试可能会用到的动态规划
by Pleiades_Antares
NOIP的例题大概会讲
NOIP2014 飞扬的小鸟
NOIP2017 宝藏
(还有更多的但是
NOIP对动态规划的考察主要在状态和转移方程的设计方面,往往设计出优秀的状态和转移即可在题目中取得不错的分数。
一、树形DP
树形DP用于解决树上问题。
状态常常用f(u,S)来表示已经对子树u这个子问题进行求解,状态为S的值。
转移往往会有子树合并。

如上图所示,可以考虑通过这个我手绘的图片来辅助理解。

其代码常常如下所示:
dfs(u)
initial f(u)
foreach v in son[u]
dfs(v)
update f(u) with f(v)
解释下:
树的直径:

两个画了圈的点的距离即为树的直径。
f(u)表示以u为根的子树,到u的最长链的长度
来一道例题热身:
有一颗n个节点n-1条边的无向树,树上节点用1,2,.....n编号。
初始时每个节点都是白色。如果选中了节点u,则对于树中的每条边(u,v),v都会被染成黑色。注意u自身不会被染黑。
现在总共要选择恰好k个点,问有多少种方法使得所有节点被染黑。
数据范围:1<=n<=100000,1<=k<=min(n,100)
先考虑n<=1000时要怎么做?
令dp(u,k,color,choice)表示对于以u为根的子树,里面选了k个点,u的颜色为黑/白,u有没有被选中。u子树外点的选择只能影响到u子树中u点的颜色,
u子树只有u点的选择情况能影响到u子树外的点的颜色。所以这样的状态时足够的。
时间复杂度:O(nk2)
dp[u][k][color][choice]
t[k][color][choice] void dfs(int u) {
dp[u][][][] = ;
dp[u][][][] = ;
for (int v: son[u]) {
dfs(v);
for (int uk = ; uk <= k; ++uk) {
for (int vk = ; vk <= k; ++vk) {
for (int ucolor = ; ucolor < ; ++ucolor) {
for (int vcolor = ; vcolor < ; ++vcolor) {
for (int uchoice = ; uchoice < ; ++uchoice) {
for (int vchoice = ; vchoice < ; ++vchoice) {
t[uk + vk][u_new_color][uchoice] += dp[u][uk][ucolor][uchoice] * dp[v][vk][vcolor][vchoice]
}
}
}
}
}
}
}
}
//尝试在用u的孩子v的dp数组f(v)更新f(u)的时候,只枚举f(u)
实际上,时间复杂度是O(nk)而非O(nk2)
这就是——01背包实际上。
关于01背包的模板可以参考这篇文章,网上也有很多其他大神有关于01背包的理解,这里就不再赘述。
把std源代码发出来:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + , maxk = , mod = 1e9 + ;
void judge() {
freopen("action.in", "r", stdin);
freopen("action.out", "w", stdout);
return;
}
int n, k, siz[maxn], q[maxn], par[maxn], fnt, rar, ans;
int dp[maxn][maxk][][], pd[maxk][][];
vector<int> g[maxn];
inline void Add(int &x, int y) {
x = x + y < mod ? x + y : x + y - mod;
return;
}
int main() {
judge();
scanf("%d%d", &n, &k);
for (int i = ; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
q[rar++] = ;
while(fnt != rar) {
int u = q[fnt++];
siz[u] = ;
dp[u][][][] = dp[u][][][] = ;
for (int i = ; i < g[u].size(); ++i) {
int &v = g[u][i];
if(v != par[u]) {
par[q[rar++] = v] = u;
}
}
}
for (int ti = rar - ; ti; --ti) {
int &u = q[ti], &p = par[u], Endu = min(siz[u], k), Endp = min(siz[p], k);
for (int ip = ; ip <= Endp; ++ip) {
for (int xp = ; xp < ; ++xp) {
for (int yp = ; yp < ; ++yp) {
pd[ip][xp][yp] = dp[p][ip][xp][yp];
dp[p][ip][xp][yp] = ;
}
}
}
for (int ip = ; ip <= Endp; ++ip) {
for (int xp = ; xp < ; ++xp) {
for (int yp = ; yp < ; ++yp) {
static int s;
if(s = pd[ip][xp][yp]) {
int End = min(Endu, k - ip);
for (int iu = ; iu <= End; ++iu) {
for (int xu = ; xu < ; ++xu) {
for (int yu = xp ^ ; yu < ; ++yu) {
Add(dp[p][iu + ip][xp][yp | xu], (long long) s * dp[u][iu][xu][yu] % mod);
}
}
}
}
}
}
}
siz[p] += siz[u];
}
for (int xu = ; xu < ; ++xu) {
Add(ans, dp[][k][xu][]);
}
printf("%d\n", (ans + mod) % mod);
return ;
}
二、数位DP
如果题目给定了一个很大的数字,问你有多少符合一系列与
例题:
定义 S(n) 为将 n在 10 进制下的所有数位从小到大排序后得到的数。例如:S(1)=1, S(50394)=3459, S(323) =233
给定 X 求
取模的结果。
数据范围:1<=X<=10700.
考虑如何计算答案,可以通过分别计算每一位对总和的贡献来求。定义 cnt(i, x)为 S(1),S(2),...,S(X)中第i位为x 的数量。
直接求 cnt(i, x)仍然较为困难,考虑差分。令dlt(i, x) 为第 i 位上有多少个数 ≥ x。对于一个 S(y) 中第 i 位 ≥ x 的数 y,可以发现其必须满足有至少 i 个数位 ≥ x,这样就可以进行dp了。
另 dp(i, j, x, cmp) 表示填了前 i位,有至少 j个数字≥ x,与 N 的大小关系位cmp。
转移时直接枚举第i + 1 位数字是多少即可。
三、状压DP
解决状态较为复杂的问题,时间复杂度常常是指数级别。

(qwq我真的列不出来只能放截图了)
S = << n;
for (int s = ; s < S; ++s) {
for (int t = s; ; t = (t - ) & s) {
...
if(t == ) break;
}
}
来一道例题

再来一道例题:
NOIP2014
飞扬的小鸟
思路详解⬇️

NOIP2017 宝藏
先看一下题目:
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nn 个深埋在地下的宝藏屋,也给出了这 nn个宝藏屋之间可供开发的 mm 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。
新开发一条道路的代价是:
这条道路的长度 xx 从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋)。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <vector>
#include <utility>
#include <string>
#include <functional> #define rep(i, n) for(int i = 0; i < (n); ++i)
#define forn(i, l, r) for(int i = (l); i <= (r); ++i)
#define per(i, n) for(int i = (n) - 1; i >= 0; --i)
#define nrof(i, r, l) for(int i = (r); i >= (l); --i)
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(), (x).end()
#define mp make_pair
#define pb push_back
#define X first
#define Y second using namespace std; typedef long long LL;
typedef pair<int, int> pii; const int maxn = , maxs = << , oo = 1e9 + ; int n, m, g[maxn][maxn], h[maxn][maxs];
int dp[maxn][maxs], f[maxs][maxs]; void judge() {
freopen("treasure.in", "r", stdin);
freopen("treasure.out", "w", stdout);
return;
} inline bool chkmin(int &x, const int &y) {
return x > y ? x = y, : ;
} int main() {
// judge();
scanf("%d%d", &n, &m);
if(n == ) {
puts("");
return ;
}
rep(i, n) {
rep(j, n) {
g[i][j] = oo;
}
g[i][i] = ;
}
while(m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
--u;
--v;
if(w < g[u][v]) {
g[u][v] = g[v][u] = w;
}
}
m = << n;
rep(i, n) {
rep(s, m) {
if((s >> i) & ) {
continue;
}
h[i][s] = oo;
rep(j, n) {
if((s >> j) & ) {
chkmin(h[i][s], g[i][j]);
}
}
}
}
rep(s, m) {
int T = m - ^ s;
for (int t = T; t; t = (t - ) & T) {
rep(i, n) {
if((s >> i) & ) {
if(h[i][t] == oo) {
f[s][t] = oo;
break;
}
f[s][t] += h[i][t];
}
}
}
}
rep(i, n) {
rep(s, m) {
dp[i][s] = oo;
}
}
rep(i, n) {
dp[][ << i] = ;
}
int tmp;
rep(i, n - ) {
rep(s, m) {
if((tmp = dp[i][s]) < oo) {
// printf("dp[%d][%d] = %d\n", i, s, tmp);
int T = m - ^ s;
for (int t = T; t; t = (t - ) & T) {
if(f[t][s] != oo) {
chkmin(dp[i + ][s | t], tmp + f[t][s] * (i + ));
}
}
}
}
}
--m;
int ans = oo;
rep(i, n) {
chkmin(ans, dp[i][m]);
}
printf("%d\n", ans);
return ;
}
有 30000个岛屿从左到右排列,有 n个宝石,在 p1, p2, ..., pn上。 你初始时在 0 号岛上,第一次跳到 d 号岛上,第i (i > 2)次你向右跳跃的距离为第i − 1次跳跃距离 l 或者 l-1 或者l + 1。问最多能拿多少宝石。
数据范围:1 ≤ n, d ≤ 30000。
再看一道题:


TG可能会用到的动态规划-简易自学的更多相关文章
- PJ可能会用到的动态规划选讲-学习笔记
PJ可能会用到的动态规划选讲-学习笔记 by Pleiades_Antares 难度和速度全部都是按照普及组来定的咯 数位状压啥就先不讲了 这里主要提到的都是比较简单的DP 一道思维数学巧题(补昨天) ...
- 在自学java路上遇上的南墙
从2016年12月20号自学java,先是咨询了下培训中心,得小两万,四个月毕业,算了一笔账,一百二十天,合下来每天三百多块,再加上开销之类压力太大,于是开始入坑自学,随后血一般的教训直面而来: 1. ...
- [LeetCode] 系统刷题5_Dynamic Programming
Dynamic Programming 实际上是[LeetCode] 系统刷题4_Binary Tree & Divide and Conquer的基础上,加上记忆化的过程.就是说,如果这个题 ...
- PJ考试可能会用到的数学思维题选讲-自学教程-自学笔记
PJ考试可能会用到的数学思维题选讲 by Pleiades_Antares 是学弟学妹的讲义--然后一部分题目是我弄的一部分来源于洛谷用户@ 普及组的一些数学思维题,所以可能有点菜咯别怪我 OI中的数 ...
- 动态规划TG.lv(1) (洛谷提高历练地)
动态规划TG.lv(1) P1005 矩阵取数游戏 分析:每行不超过80个数字,直接区间DP即可,\(dp[i][j]\)表示区间\([i,j]\)之间取数可以得到的答案,每次向右或者向左扩展即可.但 ...
- Spring框架自学之路——简易入门
目录 目录 介绍 Spring中的IoC操作 IoC入门案例 Spring的bean管理配置文件 Bean实例化的方式 Bean标签的常用属性 属性注入 使用有参构造函数注入属性 使用set方法注入属 ...
- 自学WEB前端到什么程度才能就业
做过多年web前端从业者,回答下这个问题 首先,这个问题主要问:自学web前端技术,如果才能找到一份web前端的工作.按照现在的招聘标准来看,无论你去哪个公司面试,你只需要满足他们公司的需求就可以. ...
- MIT挑战(如何在12个月内自学完成MIT计算机科学的33门课程|内附MIT公开课程资源和学习顺序
译者注:本文译自Scott H. Young的博客,Scott拥有超强的学习能力,曾在12个月内自学完成麻省理工学院计算机科学的33门课程.本文就是他个人对于这次MIT挑战的介绍和总结. 版权声明:本 ...
- 自学Java,需要掌握什么内容才能找到满意的工作?
首先,这个问题主要问:自学Java编程技术,如果才能找到一份Java编程的工作.按照现在的招聘标准来看,无论你去哪个公司面试,你只需要满足他们公司的需求就可以. 找到一份Java编程工作需要掌握的内容 ...
随机推荐
- 从零开始学 Web 之 jQuery(八)each,多库共存,包装集,插件
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- C#7.0--引用返回值和引用局部变量
一.在C#7.0以上版本中,方法的返回值可以通过关键字ref指定为返回变量的引用(而不是值)给调用方,这称为引用返回值(Reference Return Value,或ref returns): 1. ...
- Django之模型层(单表操作)
一.ORM简介 MVC和MTV框架中包含一个重要部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库. ORM是‘对象-关系- ...
- RPA流程自动化-Blueprism认证考试介绍
RPA流程自动化-Blueprism认证考试介绍 接触RPA有一段时间了,几种RPA相关工具也都试用过,BluePrism是RPA工具的一种,今天跟大家分享考Blueprism的一些经验. RPA(R ...
- Oracle,MySQL,sqlserver三大数据库如何获取系统当前时间
Oracle中如何获取系统当前时间:用SYSDATE() MySQL中获取系统当前时间主要有以下几点: (1)now()函数以('YYYY-MM-dd HH:mm:SS')返回当前的日期时间,可以直接 ...
- 深入理解RDD原理
首先我们来了解一些Spark的优势:1.每一个作业独立调度,可以把所有的作业做一个图进行调度,各个作业之间相互依赖,在调度过程中一起调度,速度快.2.所有过程都基于内存,所以通常也将Spark称作是基 ...
- MATLAB中eig的作用
MATLAB中eig的作用 eig Find eigenvalues and eigenvectors 返回矩阵的特征值和特征向量; [V,D] = eig(A) D是特征值 V特征向量 例 ...
- API接口规范V1.0——制定好规范,才好合作开发
返回码规范: 统一六位 000000 表示成功! 参数相关返回码预留100000-199999:系统相关返回码预留200000-299999:数据中心310000-319999后续项目以此类推,后续根 ...
- SQL Server 连接(内连接,外连接,完全连接,交叉连接,联合)
1.连接 有时候需要将连个表的数据合并成一个结果集来显示.为了解决这个问题,就需要用到JOIN连接. 2.内部连接 内部连接根据一个或几个共同的字段将记录匹配到一起.内部连接仅仅返回那些存在字段匹配的 ...
- 【代码笔记】Web-HTML-表单
一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
取模的结果。