[DynamicProgramming]动态规划题目泛做

Educational Codeforces Round 12 F

大意

求n(n<=1011)以内恰好有4个因数的数的个数

分析

首先一个数恰好有4个因数,说明它质因数分解之后是两个质数的乘积或是一个质数的三次方,对于后一种情况我们直接n1/3就能算出来,关键在于计算n以内有多少个数是两个素数的乘积。

设n=p1?p2,则必然有p1<n?√,p2>n?√,我们枚举p1,那么问题就在于np1 内有多少个素数。

这一点我们用dp来解决。

记pj为第j个素数,dp[n][j]为[1,n]以内最小质因子>=pj的数的数量,我们有

1.dp[n][1]=n

2.dp[n][j]=dp[n][j+1],dp[?n/pj?][j],

也就是说dp[n][j+1]=dp[n][j]?dp[?n/pj?][j]

令pk是最小的大于n?√的素数,那么π(n)=dp[n][k]+k?1

Codeforces Round #346 G

大意:

给n(n≤106)个长度为hi的竖方格,允许取走连续若干个竖方格的顶部(不允许取最底层),求方案数

分析:

记dp[l][r]为取走的区间为[l,r]的方案数,总方案数就是

ans=∑l=1n∑r=lndp[l][r]

l=r时,dp[l][l]=hl?1

l<r时,dp[l][r]=min(hl?1,hl+1?1)?Πr?1i=l+1min(hi?1?1,hi?1,hi+1?1)?min(hr?1?1,hr?1)

那么我们有

ans=∑ni=1(hi?1)+∑nl=1∑nr=l+1min(hl?1,hl+1?1)?Πr?1i=l+1min(hi?1?1,hi?1,hi+1?1)?min(hr?1?1,hr?1)

关键在于后一个长长的式子,即

∑nl=1∑nr=l+1min(hl?1,hl+1?1)?Πr?1i=l+1min(hi?1?1,hi?1,hi+1?1)?min(hr?1?1,hr?1)

=∑nr=2min(hr?1?1,hr?1)?∑r?1l=1min(hl?1,hl+1?1)?Πr?1i=l+1min(hi?1?1,hi?1,hi+1?1)

令S(r)=∑r?1l=1min(hl?1,hl+1?1)?Πr?1i=l+1min(hi?1?1,hi?1,hi+1?1)

则S(r+1)=S(r)?min(hr?1?1,hr?1,hr+1?1)+min(hr?1,hr+1)?1

然后就可以线性做了


Codeforces Beta Round #2 B

大意

给一个n?n的矩阵,每个格子有一个数,从左上到右下,只许向右或向下,求一条乘积末尾0最少的路径

分析

考虑每个数质因数分解,末尾0只会来自于2*5,因此我们只要最小化路径上2和5的数量,不难发现2和5分别独立,最优解必然选择2和5中的更优解,问题转化为包含2最少的路径,记状态为dp[i][j],直接转移即可


Codeforces Beta Round #5 C

大意

给一个括号序列,求一个最长合法括号子串

分析

(分类是dp不知道为什么)用一个栈搞一搞就行了。碰到左括号就入栈,右括号就弹栈顶,弹的时候更新答案

Educational Codeforces Round 12 C

大意

给一个小写字母串s(1<=|s|<=2?105),一次操作可以改变一个字母,求最少操作次数使得相邻两个字符不同,求最终的串

分析

(分类是dp不知道为什么)显然对于一段相同的,假如说有k个,我们最少只需要?k2?次就能使他们相邻的都不一样。

Educational Codeforces Round 11 C

大意

给一个长度为n(n≤3?105)的01序列,允许k次将某一个0变成1,求最大化最长连续全1子序列长度之后的序列

分析

(分类又是dp不知道为什么)注意到我们操作的本质是选一段区间里面0的数量不超过k然后把它全部变成1,如果区间[l,r]可行,那么[l+1,r]必然可行, 我们用twopointers就行了


Codeforces Beta Round #8 C

大意

给出n(n<=24)个物品的坐标和箱子的坐标,人从箱子开始,每次最多运两个东西,必须把东西放回箱子,最后在箱子结束,求最小总移动距离。

分析

n很小,我们直接状压,dp[S]表示目前还没放回的物品的状态集合,并且此时人在箱子的最小移动距离,n2转移,看起来会T,实际上不满。

CROC 2016 - Final Round C

大意:

给一个n行m列的01矩阵(1<=n<=20,1<=m<=100000),一次操作可以将某一行或某一列的01全部反转,求经过不限次数的操作之后最少还剩几个1

分析

显然我们最多只会操作n+m次,也就是把每行每列都翻一次,将一行翻偶数次等于没翻。

首先我们有一个O(2n?m)的算法,我们枚举每一行是否翻,然后再一列一列考虑,如果能使1更少就翻。

我们记coli表示第i行一开始的状态,mask表示列的翻转状态,那么上面算法的方程就是

∑mi=1min(popcount(maskxorcoli),n?popcount(maskxorcol[i]))

注意到我们不关心每一行异或的值具体是多少,我们只关心里面1的个数,因此我们记dp[k][mask]为[1,m]中有多少列与mask异或以后有恰好k个1,那么我们要求的式子就变成了

∑k=0ndp[k][mask]?min(k,n?k)

现在的问题是如何计算dp[k][mask]考虑一个coli与mask在第p位不同,那么makexorp与coli恰好有k?1位不同,这种列的数量为dp[k?1][maskxorp],但这里面也包含了与maskxorp在第p位不同的列,我们需要再去掉dp[k?2][maskxorp]……以此类推,我们相当于是对第p位做了容斥

BZOJ 2004 [Hnoi2010]Bus 公交线路

大意

要求给n(n≤109)个格子染K种颜色,每个格子只能染一种颜色,同一种颜色间隔不能超过P(P≤10),要求最先P个和最后P个每种颜色恰好出现一次,求方案数

分析

像这样间隔不超过一个很小的数的条件,往往启发我们对前面若干位进行状态压缩。

在本题中,对于每个格子,我们只关心它前P个格子的染色状况,因此我们考虑对前P个格子进行状压,

记录各种颜色已经从左往右染到了哪里。

如果就这样直接转移,很快我们会发现这样会导致重复计数,并且有些格子可能根本没有被染色,因此我们需要加强对转移和状态的限制,我们限制状态的最高位必须为1,转移时必须从这个1开始转移。

BZOJ2073 [POI2004]PRZ

大意:

将一群人分为若干组过桥,一组人的总重不能超过一个数,一组人过桥耳朵时间是这组人里面最慢的那个,最小化所有人过桥时间,人数≤16

分析:

人数很小,我们对每个人是否已经被分配组状态压缩,对于一个状态,我们需要枚举它的一个子集进行转移。

枚举子集可以这样方便的实现

for(int j = i; j; j = i & (j - 1))

其中i是全集,j不重不漏地枚举了所有子集

可以证明枚举子集的复杂度是O(3n)


Codeforces Beta Round #8 E

大意

考虑长度为n(n<=50)的所有01串,两个01串本质相同当且仅当它们可以通过01互变或串反转得到对方,求字典序第k(k<=1016)小的与小于它的所有串本质不同的串。

分析

对于这种求字典序K小的合法串的问题,我们一般考虑逐位确定

显然和一个01串本质相同的串最多只有3个,分别是串反转,01反转,串反转+01反转,而我们的答案要求的就是第k个小于和它本质相同的三个串的01串。

假设当前已经确定的前缀为pre,我们需要计算出前缀为pre0的合法串是否满k个。我们考虑用数位dp来进行计数。

记dp[l][f1][f2]表示当前为第l位,是否小于串反转,是否小于01反转,枚举第l和n+1?l位是0/1进行转移,记忆化搜索实现。

BZOJ 4521 [Cqoi2016]手机号码

大意

求[L,R]以内至少出现了一次相邻的相同的三个数字,不同时出现8和4的11位数的数量

分析

典型的数位dp,状态dp[dep][last][cnt][cons][eight][four][lim]

表示第dep位 上一个数是last 上一个数连续了cnt次 是否已经有过相邻的三个相同数字,是否出现过8,是否出现过4,已知前缀是否等于上界,枚举下一位数字,合法则转移,记忆化搜索实现。

BZOJ 3209 花神的数论题

大意

令Sum(i)为i在二进制下1的个数 求∏ni=1Sum(i)

分析:

我们变换一下思路,转而求[1,n]以内二进制位恰好有k个1的数有几个。

那么这个问题就可以数位dp了


Codeforces Beta Round #9 D

大意

n个点的深度不低于h的不同的二叉搜索树的数量(n<=35) 键值为[1,n]

分析

首先我们将深度不低于h转化为深度恰好h,记dp[n][h]为n个点深度恰好为h的二叉搜索树的数量。

显然dp[0][0]=1,否则dp[i][0]=dp[0][i]=0

考虑任意一棵n个点深度为h的 二叉搜索树,假如根上的数是m,则左子树有m?1个点,右子树有n?m个点,两者之间深度大的深度恰好为h?1,这时就有两种情况

1.左子树深度恰好为h?1,左子树的合法数量为dp[m?1][h?1],右子树深度随意,为∑h?1h′=0dp[n?m][h′],乘法原理即可得到这种情况的合法数量

2.右子树深度恰好为h?1,这与情况1其实差不多,不多赘述。

然后我们就可以求出dp[n][h]了

BZOJ 1040 ZJOI2008 骑士

大意

求一个环套树的最大点权独立集

分析

一般来说,环套树上的问题我们总考虑选取一条边然后破环转化为一般树的问题。

记f[i]为以i为根的子树在取i点的情况下的最大权值,g[i]为不取

取环上任意一边破环,设两边为u,v

于是有两种情况

u不取,v任意,我们以u为根跑一遍树形dp,取g[u]

v不取,u任意,我们以v为根跑一遍树形dp,取g[v]

Tips: 一般树最大点权独立集

记f[i]为以i为根的子树在取i点的情况下的最大权值,g[i]为不取

我们有

f[i]=w[i]∑son[i]g[son[i]]

g[i]=∑son[i]max(f[son[i]],g[son[i]])

BZOJ 3037 创世纪

大意

求一个环套树的最小支配集

分析

一般来说,环套树上的问题我们总考虑选取一条边然后破环转化为一般树的问题。

设f[x]为以x为根的子树选择x的最小支配集,g[x]为不选择x的最小支配集

取环上任意一边破环,设这个点为x 出边指向的点为y

讨论

1.若x选择 则y一开始就是被支配状态 g[y]初值为0 求一遍最小支配集

2.若x不选正常求最小支配集即可

Tips: 一般树最小支配集

和最大点权独立集状态类似只不过有三种状态,一是i在支配集中,二是i被至少一个儿子支配,三是i被父亲支配

BZOJ 4033 HAOI2015 T1

大意

给定一棵树,你需要把其中的k个点染成黑色,使得黑色点两两之间的距离和+白色点两两之间的距离和最大,求最大值

分析

用f[i][j] 表示在以i 为根的子树,有 j个黑点的最大权值。

这个权值指的是,这个子树内部的点对间距离的贡献,以及i和fa[i]之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值)。

然后DFS来求,枚举i的每个儿子j,现在的f[i][]是包含了[1,j?1]子树,然后两重循环枚举范围是[1,j?1]的子树总Size和j的 Size,来更新f[i][],这样更新之后的f[i][]就是[1,j] 子树的答案了。

BZOJ 1131 POI2008 Sta

大意

给定一个n个点的无根树,要求找到一个根节点,使深度之和最大

分析

对于这种无根树换根的问题,我们一般考虑做两遍dp,第一遍任意选择一个点为根dp出子树信息,从儿子推父亲,第二遍dp出某一个点为根时其它子树的信息,从父亲推儿子

在本题中,我们先把这棵树处理成以1为根的有根树,维护以每个点为根的子树的节点数size[],我们逐层O(1)查询,设a=fa[b],且已知ans[a],那么从a为根转为b为根,b的子树中的点深度都?1,其他点深度都+1,所以得到递推式ans[i]=ans[fa[i]]?size[i]+n?size[i]

Codeforces Beta Round #14 E

大意

给一棵n(n<=200)个点的树,求两条不相交路径使得路径总长最大

分析

观察发现两条不相交路径必然可以通过割断树上的某条边之后分别处于割出来的两个子树里,因此我们枚举割断某条边,dp出两侧的最长路,这种问题我们也需要两遍dp,一遍父亲推儿子,一遍儿子推父亲

BZOJ 2286 SDOI2011 消耗战

大意:

给定一棵树,边上有边权,m次询问,每次选定一些关键点,求将1号节点与所有关键点都切断所需的最小花销,询问总点数<=5?105

分析:

首先我们需要用到一种新的武器——虚树

虚树是这样一棵树:在原树上选出若干关键点,将没有分叉的边全部压缩成一条边,从而得到一棵与关键点数量成线性的树

对于像该题的问题来说,本来我们应该对于每个询问都对整棵树重新做一边树形dp,但这样显然太费时。

对于每个询问,我们如果能够建出它对应的虚树,那么在虚树上dp的总复杂度就只与总询问点数有关,大大降低了时间复杂度

如何维护虚树呢?我们这样做

维护一个栈,栈中的元素形成一条由根节点出发的链,初始栈中只有根节点

将所有关键点按照DFS序排序

每次加入一个节点,求出节点与栈顶的LCA,将栈中所有深度大于LCA的节点全都弹掉

然后将LCA和该节点入栈,注意有些重复的情况要考虑

知道如何维护虚树之后,我们继续考虑dp的问题

令f[x]表示切断以x为根的子树中所有关键点的最小花销

g[x]表示x是不是关键点

那么对于x的每个子节点y有f[x]=∑min(g[y]?INF:f[y],Distance(x,y))

然后我们直接在虚树上dp就可以了。

BZOJ 3162 独钓寒江雪

大意:

给定一棵树,求本质不同的独立集个数对1000000007取模后的值

分析:

首先这里涉及到一个问题——本质不同

因此首要要解决的问题是无根树同构

我们给出如下基本知识:

1.直径是树上最远点对之间的路径。

2.取直径的中点称为中心。直径长度为偶数时,中心在结点上;直径长度为奇数时,中心在边上。

3.树上的所有直径都过中心。

4.找一条直径的方法是从任意一个点 bfs 到树上最远点 x,然后从 x bfs 找到最远点 y x到y 的路径一定是直径。

5.无论标号如何,树的中心位置是确定的。

通过寻找重心我们可以将无根树同构转化为有根树同构,判断有根树同构最基本的方式是最小表示法

定义S[v] 表示以v为根的子树的括号序列。

c1,c2,???,ck 为v 的 k 个儿子,按照 S[c1],S[c2],???,S[ck] 的字典序对这些儿子重新进行排序。

S[v]=‘‘(”+S[c1]+S[c2]+???+S[ck]+‘‘)”

我们规定了子树的顺序(即按字典序的顺序),所以同构的树的括号序列表示具有唯一性。

而我们发现直接使用括号序列太慢了,所以我们可以使用字符串哈希

然后我们就可以判断树的同构问题了。

考虑一个点i,首先我们将同构的儿子并在一起,用组合数计算出它们之间本质不同的方案数,然后对于不同构的儿子直接乘法原理即可

to be continued…

时间: 2024-08-21 01:03:07

[DynamicProgramming]动态规划题目泛做的相关文章

小题目泛做

题目1 CodeForces 605 A 题目大意:给定一个长度为n的序列,每次可以把一个数放到开头或者最后,求升序排好的最小次数. 求出原序列位置的最长上升子序列,用n减去最大的.我们只要保证相对大小不变就可以了. Code: 1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstdlib> 5 #include <cstring> 6 7

最大权闭合子图题目泛做

题目1 POJ2987 题目大意: 一个公司要裁员,每个成员都有自己的效益值,可正可负,而且每个人都有自己的直接下属,如果某个人被裁员,那么他的直接下属,他的下属的下属....都会离开这家公司. 现在请你确定裁员的方案,求最小裁员人数和公司的最大收益. 算法讨论: 选了一个点,其后继都必须要选,这是闭合子图的特点.所以这个题就是裸题啦. 最小裁员人数就是与S相连的点数,公司的最大收益就是正权和-最小割. Code: 1 #include <cstdlib> 2 #include <cst

网络流题目泛做(费用流的不写在这里面)

题目1 ZJOI 最小割 题目大意: 求一个无向带权图两点间的最小割,询问小于等于c的点对有多少. 算法讨论: 最小割 分治 代码: #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int N = 15

可持久化数据结构题目泛做。

个人理解: 每个新的线段树的一个结点保存的是1...位置 i中的数字在相应的区间上有几个. 然后我们用r-(l-1)得到的就是l...r上的中字在相应的区间中出现了几个. 题目1 POJ2104 题目大意:静态查询区间第K小值. 裸的可持久化线段树. 1 #include <cstdlib> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstr

二分图题目泛做(为了对应陈学姐的课件)

题1:BZOJ1854 SCOI2010游戏 每个属性x,y分别向i连条边,匹配到不能匹配为止. 输出即可. 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int N = 1000000 + 5; 10 11 i

noi往届题目泛做

noi2015 Day1 t1 程序自动分析  离散化+并查集 t2 软件包管理器  裸树链剖分 t3 寿司晚宴  状压dp Day2 t1 荷马史诗 哈夫曼多叉树 t2 品酒大会 后缀数组按照height排序+并查集 t3 小园丁与老司机 noi2014 Day1 t1 起床困难综合症  按位dp,贪心 t2 魔法森林 link-cut-tree维护MST t3 题答 Day2 t1 动物园 KMP简单题 t2 随机数生成器  贪心,每一行的区间是连续不跨越的,卡内存 t3 购票 树链剖分+线

历年NOIP水题泛做

快noip了就乱做一下历年的noip题目咯.. noip2014 飞扬的小鸟 其实这道题并不是很难,但是就有点难搞 听说男神错了一个小时.. 就是$f_{i,j}$表示在第$i$个位置高度为$j$的时候最小点击次数 递推的话对于上升的情况只做一次,后面几次在后面再做.. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace st

动态规划题目

动态规划算法,在T大某位老师的书中说就是递推+重复子问题. 动态规划算法的效率主要与重复子问题的处理有关. 典型的题目有 陪审团,最大公共子串问题 1,最大公共子串问题 这个是动态规划的基础题目.动态规划就是递推和重复子结构. 确定了递推关系后.找到一个能极大地减少重复运算的子结构至关重要.选的好了,时间效率会很好. 这个问题,不妨设第一个串为a,长度为n,第二个串为b,长度m.那么最长的子序列长度为f(n,m) 当a[n]=a[m]时 f(n,m)=1+f(n-1,m-1) 否则f(n,m)=

poj 动态规划题目列表及总结

此文转载别人,希望自己能够做完这些题目! 1.POJ动态规划题目列表 容易:1018, 1050, 1083, 1088, 1125, 1143, 1157, 1163, 1178, 1179, 1189, 1208, 1276,1322, 1414, 1456, 1458, 1609, 1644, 1664, 1690, 1699, 1740(博弈),1742, 1887, 1926(马尔科夫矩阵,求平衡), 1936, 1952, 1953, 1958, 1959, 1962, 1975,