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编程工作需要掌握的内容 ...
随机推荐
- Solidity中uint转bytes
Solidity中uint转bytes方法如下: pragma solidity ^0.4.2; contract Test { function toBytesNickJohnson(uint256 ...
- apache tomcat搭建负载均衡(实现集群中的session同步)
原理:tomcat 做个WEB服务器有它的局限性,处理能力低,效率低.承受并发小(1000左右).但目前有不少网站或者页面是JSP的.并采用了tomcat做为WEB,因此只能在此基础上调优. 目前采取 ...
- T-SQL:是NULL不是NULL(七)
首先SQL SERVER 是一个三值逻辑 即谓词计算结果为TRUE,FALSE,UNKNOWN 标准的谓词都是遵循这种规则的 如 slary>0 会返回计算结果为TRUE 结果的行拒绝FALS ...
- c#实战开发:以太坊Geth 常用命令 (四)
首先运行客户端 当前命令分为 eth,web3 ,personal ,net 输入 >eth 可以看到该命令下的所有方法 > eth 1.创建用户 personal.newAccount ...
- Java - ConcurrentMap原理
https://blog.csdn.net/justloveyou_/article/details/72783008 结构是怎样的?segment是什么?hashEntry是什么?默认可并发的大小是 ...
- 使用Via浏览器+ADM下载器突破百度网盘下载限速
1.下载必要工具 via浏览器 ADM下载器 2.自定义 UA UA 是一串特殊字符,用来告诉所访问的网站,手机使用的操作系统及版本.CPU 类型.浏览器及版本等信息.UA 内容如下: Mozilla ...
- 能用HTML/CSS解决的问题,就不要用JS
原因:简单. 简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验. 一,导航高亮 效果图: 代码: <!DOCTYPE html> <html lang=" ...
- Spark jdbc postgresql数据库连接和写入操作源码解读
概述:Spark postgresql jdbc 数据库连接和写入操作源码解读,详细记录了SparkSQL对数据库的操作,通过java程序,在本地开发和运行.整体为,Spark建立数据库连接,读取数据 ...
- 安卓开发ScrollView嵌套ListView只显示一行
在用列表控件做一个“更多功能”的界面的时候 <?xml version="1.0" encoding="utf-8"?> <ScrollVie ...
- ionic打包报错Execution failed for task ':processDebugResources'
ionic 打包的时候报了这样一个错误:Execution failed for task ':processDebugResources' 分析: compile "com.android ...