码迷,mamicode.com
首页 > 其他好文 > 详细

[DynamicProgramming]动态规划题目泛做

时间:2016-04-26 21:38:41      阅读:370      评论:0      收藏:0      [点我收藏+]

标签:

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(n106)个长度为hi的竖方格,允许取走连续若干个竖方格的顶部(不允许取最底层),求方案数
分析:
dp[l][r]为取走的区间为[l,r]的方案数,总方案数就是

ans=l=1nr=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=1nr=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=1nr=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(n3?105)01序列,允许k次将某一个0变成1,求最大化最长连续全1子序列长度之后的序列
分析
(分类又是dp不知道为什么)注意到我们操作的本质是选一段区间里面0的数量不超过k然后把它全部变成1,如果区间[l,r]可行,那么[l+1,r]必然可行, 我们用twopointers就行了


Codeforces Beta Round #8 C

大意
给出nn<=24个物品的坐标和箱子的坐标,人从箱子开始,每次最多运两个东西,必须把东西放回箱子,最后在箱子结束,求最小总移动距离。
分析
ndp[S]表示目前还没放回的物品的状态集合,并且此时人在箱子的最小移动距离,n2移,看起来会T,实际上不满。

CROC 2016 - Final Round C

大意:
给一个nm列的01矩阵(1<=n<=201<=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异或以后有恰好k1,那么我们要求的式子就变成了

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

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

BZOJ 2004 [Hnoi2010]Bus 公交线路

大意
要求给nn109个格子染K种颜色,每个格子只能染一种颜色,同一种颜色间隔不能超过P(P10)PP,求方案数
分析
像这样间隔不超过一个很小的数的条件,往往启发我们对前面若干位进行状态压缩。
在本题中,对于每个格子,我们只关心它前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串。
假设当前已经确定的前缀为prepre0k个。我们考虑用数位dp来进行计数。
dp[l][f1][f2]l,是否小于串反转,是否小于01反转,枚举第ln+1?l0/1行转移,记忆化搜索实现。

BZOJ 4521 [Cqoi2016]手机号码

大意
[L,R]以内至少出现了一次相邻的相同的三个数字,不同时出现8411数的数量
分析
典型的数位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]以内二进制位恰好有k1的数有几个。
那么这个问题就可以数位dp了


Codeforces Beta Round #9 D

大意
nh的不同的二叉搜索树的数量n<=35 键值为[1,n]
分析
首先我们将深度不低于h转化为深度恰好h,记dp[n][h]为n个点深度恰好为h的二叉搜索树的数量。
显然dp[0][0]=1,否则dp[i][0]=dp[0][i]=0
考虑任意一棵nh 二叉搜索树,假如根上的数是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]iig[i]
取环上任意一边破环,设两边为u,v
于是有两种情况
uvu为根跑一遍树形dp,取g[u]
vuv为根跑一遍树形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]为以xx支配集,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个黑点的最大权值。
这个权值指的是,这个子树内部的点对间距离的贡献,以及ifa[i]之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值)。
然后DFSijf[i][][1,j?1][1,j?1]Sizej Size,来更新f[i][]f[i][][1,j] 子树的答案了。

BZOJ 1131 POI2008 Sta

大意
给定一个n个点的无根树,要求找到一个根节点,使深度之和最大
分析
对于这种无根树换根的问题,我们一般考虑做两遍dp,第一遍任意选择一个点为根dp出子树信息,从儿子推父亲,第二遍dp出某一个点为根时其它子树的信息,从父亲推儿子
在本题中,我们先把这棵树处理成以1为根的有根树,维护以每个点为根的子树的节点数size[],我们逐层O(1)查询,设a=fa[b],且已知ans[a],那么从ab,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的每个子节点yf[x]=min(g[y]?INF:f[y],Distance(x,y))
然后我们直接在虚树上dp就可以了。

BZOJ 3162 独钓寒江雪

大意:
给定一棵树,求本质不同的独立集个数对1000000007取模后的值
分析:
首先这里涉及到一个问题——本质不同
因此首要要解决的问题是无根树同构
我们给出如下基本知识:
1.直径是树上最远点对之间的路径。
2.取直径的中点称为中心。直径长度为偶数时,中心在结点上;直径长度为奇数时,中心在边上。
3.树上的所有直径都过中心。
4.找一条直径的方法是从任意一个点 bfs 到树上最远点 x,然后从 x bfs 找到最远点 y xy 的路径一定是直径。
5.无论标号如何,树的中心位置是确定的。
通过寻找重心我们可以将无根树同构转化为有根树同构,判断有根树同构最基本的方式是最小表示法
定义S[v] 表示以v为根的子树的括号序列。
c1,c2,???,ckvk 个儿子,按照 S[c1],S[c2],???,S[ck] 的字典序对这些儿子重新进行排序。
S[v]=(+S[c1]+S[c2]+???+S[ck]+)
我们规定了子树的顺序(即按字典序的顺序),所以同构的树的括号序列表示具有唯一性。
而我们发现直接使用括号序列太慢了,所以我们可以使用字符串哈希
然后我们就可以判断树的同构问题了。
考虑一个点i,首先我们将同构的儿子并在一起,用组合数计算出它们之间本质不同的方案数,然后对于不同构的儿子直接乘法原理即可

to be continued…

[DynamicProgramming]动态规划题目泛做

标签:

原文地址:http://blog.csdn.net/hbhcy98/article/details/51236574

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!