清北学堂2018DP&图论精讲班 DP部分学习笔记

Day 1

上午

讲的挺基础的……不过还是有些地方不太明白

例1

给定一个数n,求将n划分成若干个正整数的方案数。

例2

数字三角形

例7

最长不下降子序列

以上太过于基础,不做深入讨论


例3

给定一个数n,求将n划分成若干个正整数的方案数。

题解:

  • 定义状态
    \(dp[i][j]\)表示用不超过\(j\)的数来组成\(i\)

  • 状态转移
    \(i < j \;\;\; dp[i][j]=dp[i][i]\)
    \(i = j \;\;\; dp[i][j]=dp[i][j-1]+1\)
    \(i > j \;\;\; dp[i][j]=dp[i-j][j-1]+dp[i][j-1]\)

例4

一个人站在楼梯的第一级上,每次他可以向上走1~m级。有某些级楼梯是坏的,不能走上去。而且连续走了\(k\)\(m\)级之后你接下来的一步只能走1级。问走到第N级的方案数。

题解:

  • 定义状态
    \(dp[i][j]\)在第\(i\)级台阶上,连续走了\(j\)\(m\)

  • 状态转移
    $ \sum_{j=1}^{m-1}dp[i+j][0]+= \sum_{l=0}^{k-1}dp[i][l]$(我自己按老师的意思写的方程我自己都看不懂……)
    \(dp[i+1][0]+=dp[i][k]\)
    \(dp[i+m][l+1]+=f[i][l]\;(l\neq k)\)

例5

Codeforces 467C George and Job

给定一个长度为n的序列,从序列中选出k个不重叠且连续的m个数,要求和最大。
\(1<=m\times k<=n<=5000\)

题解:

  • 定义状态
    \(sum[]\)为前缀和,\(dp[i][j]\)选了\(j\)段,以\(i\)为结尾

  • 状态转移
    \(dp[i][j]=max(dp[i-m][j-1]+sum[i]-sum[i-m],dp[i-1][j])\)

例6

51nod 1354 选数字

当给定一个序列\(a[0],a[1],a[2],...,a[n-1]\)和一个整数\(K\)时,我们想找出有多少子序列里面的所有元素乘起来恰好等于\(K\)
方案数对\(10^9+7\)取模。
\(n <= 1000,k <= 10^8\)

不会,全程懵逼

下午

考试,估计要爆零……

嗯,60分,还不错——至少比想象中的高

考试题目

官方题解

吐槽

  • T1
    只能说我太菜了,根本不会DP,爆搜+数据特判,40分滚粗

  • T2
    我会最短路,怎么才20分?好吧,那30%\(k=1\)的测试点我承认我删边删错了。题目是双向边,我也是按双向边存的,结果删的时候只删了一条边……

  • T3
    不会,讲了也不会 (`^´)ノ

Day 2

上午

上午先讲了昨天没讲完的几道题,好吧,我太菜了,一道也不会 QAQ


接Day1 例7

例8

51nod 1294 修改数组

给出一个整数数组A,你可以将任何一个数修改为任意一个正整数,最终使得整个数组是严格递增的且均为正整数。问最少需要修改几个数?
$ n < = 100000$

题解:

这道题思路很妙。

  • \(a[\;]\)表示原序列
    首先,我们将每个数\(a[i]\)减去它们对应的下标\(i\),然后将\(< 0\)\(a[i]\)删去。因为每一个数都要是正整数,所以如果\(a[i] < i\),那它肯定不符合要求。
    然后我们再在更改后的序列上找最长不下降子序列。最后用n-最长不下降子序列的长度就OK了

例9

OpenJudge 6047 (找不到这道题 \(\rm{Orz}\))

有一块矩形大蛋糕,长和宽分别是整数\(w ,h\)。现要将其切成\(m\)块小蛋糕,每个小蛋糕都必须是矩形、且长和宽均为整数。切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕。请计算:最后得到的\(m\)块小蛋糕中,最大的那块蛋糕的面积下限。
\(w,h,m <= 20\)

题解:

  • 定义状态
    \(dp[i][j][k]\)表示长宽为\(i,j\)的蛋糕切\(k\)刀的答案

  • 边界条件
    \(dp[i][j][0]=i\times j\)

  • 状态转移
    \(dp[i][j][k]=min(max(dp[i][o][p],dp[i][j-o][k-1-p],dp[o][j][p],dp[i-o][j][k-p-1]))\)

例10

Codeforces 407B Long Path

\(n+1\)个房间,一个人在1号房间。如果这是他第奇数次到当前房间(\(i\)号),那么他会去\(pi\; (pi { <= }i)\)号房间,否则他会去\(i+1\)号房间。不管他去了那个房间,他的移动次数+1。
到达n+1号房间停止移动。问这时他的移动次数。答案对\(1000000007\)取模。
\(n <= 1000\)

题解:

  • 定义状态
    \(dp[i][0]\)表示奇数次到达\(i\)号房间,\(dp[i][1]\)表示偶数次到达\(i\)号房间

  • 状态转移
    方程不如代码好表达(不想再写一个自己都看不懂的\(\Sigma\)了),所以我就把代码给搬上来了 qwq
//a[i]是原序列,dp数组如上所说
for(int i=1;i<=n;i++){
    dp[i][0]=(dp[i-1][1]+dp[i-1][0]+1)%mod;
    for(int j=a[i];j<i;j++)
        dp[i][1]+=(dp[j][1]+1)%mod;
        dp[i][1]++;
    }
cout<<(dp[n][1]+dp[n][0])%mod;

至此,基础(?)的DP就讲完了


进入——区间DP

例1

NOIp2010 乌龟棋

在一行\(n\)个格子上进行游戏,每个格子有一个分数\(a[i]\)。你在\(1\)号格子,每次可以向前走\(1/2/3/4\)个格子,每种走法限制最多走\(b_1/b_2/b_3/b_4\)次。一次走法的分数是走过的格子的分数和。问走到n号格子的最大分数。
保证\(b_1+2\times b_2+3\times b_3+4\times b_4=n-1\)(恰好走完所有的次数)
\(n<=350,a[i]<=100,b_i<=40\)

题解:

  • 定义状态
    \(dp[i][j][k][l]\)表示各种类别的卡片分别还剩多少

  • 状态转移
    算了,本来想打方程的,懒了,丢代码吧,感受一下四维DP的魅力吧! 233
for(int i=0;i<=cards[1];i++)
  for(int j=0;j<=cards[2];j++)
    for(int k=0;k<=cards[3];k++)
      for(int l=0;l<=cards[4];l++){
          pos=1+i+j*2+k*3+l*4;
          if(i) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+mark[pos]);
          if(j) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+mark[pos]);
          if(k) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+mark[pos]);
          if(l) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+mark[pos]);
      }
cout<<dp[cards[1]][cards[2]][cards[3]][cards[4]]+mark[1];

例2

NOIp2015 子串

有两个仅包含小写英文字母的字符串\(A\)\(B\)。现在要从字符串\(A\)中取出\(k\)个互不重叠的非空子串,然后把这\(k\)个子串按照其在字符串\(A\)中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串\(B\)相等?注意:子串取出的位置不同也认为是不同的方案。输出方案数%1000000007
\(length(A) <= 1000,1 <= k <= length(B) <= 200\)

题解:

这道挺毒的,卡空间,必须用滚动数组优化

  • 定义状态
    \(dp[i][j][k][0/1]\)表示字符串\(A\)\(i\),字符串\(B\)\(j\),取出了\(k\)个字符串,第\(i\)个字符选不选

  • 边界条件
dp[0][0][0][0]=dp[1][0][0][0]=1;
  • 状态转移
    以后有代码就直接丢代码了,懒了懒了
for (int i=1;i<=n;i++,pos^=1)
  for(int j=1;j<=m;j++){
      for(int o=1;o<=k;o++){
          dp[pos][j][o][0]=dp[pos^1][j][o][1]%mod+dp[pos^1][j][o][0]%mod;
          if(a[i]==b[j])
            dp[pos][j][o][1]=dp[pos^1][j-1][o-1][0]%mod+dp[pos^1][j-1][o][1]%mod+dp[pos^1][j-1][o-1][1]%mod;
          else dp[pos][j][o][1]=0;
      }
  }
cout<<(dp[n&1][m][k][0]+dp[n&1][m][k][1])%mod;

例3

NOI1995 石子合并

N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
\(n { <= } 100\)

题解:

这个题目要求最小代价,除了最小代价,Luogu还要求求出最大代价,不过实现的方法一模一样

很经典的一道题目,区间DP入门必刷题

首先要破环为链+前缀和处理

  • 定义状态
    \(dp[i][j]\)表示合并区间\([i,j]\)的最小代价

  • 状态转移
for(int i=2*n-1;i>=1;i--)
  for(int j=i+1;j<=i+n;j++){
      dp2[i][j]=214748364;
      for(int k=i;k<j;k++)
        dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]);
  }

例4

NOIp2003 加分二叉树

设一个\(n\)个节点的二叉树\(tree\)的中序遍历为\((1,2,3,…,n)\),其中数字\(1,2,3,…,n\)为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为\(di\),\(tree\)及它的每个子树都有一个加分,任一棵子树\(subtree\)(也包含\(tree\))的加分计算方法如下:
\(subtree\)的左子树的加分\(\times subtree\) 的右子树的加分\(+subtree\)的根的分数。
若某个子树为空,规定其加分为\(1\),叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为\((1,2,3,…,n)\)且加分最高的二叉树\(tree\)。要求输出\(tree\)的最高加分
\(n {<=} 30\)

题解:

emmmm,第三次碰到这题了,到现在还记得当时爆零的屈辱

  • 定义状态
    \(dp[i][j]\)表示区间\([i,j]\)的最大得分

  • 边界条件
for(int i=1;i<=n;i++){
    dp[i][i]=num[i]; //num[ ]为原序列
    dp[i][i-1]=1;
}
  • 状态转移
for(int i=n;i>=1;i--)
  for(int j=i+1;j<=n;j++)
    for(int k=i;k<=j;k++)
      if(dp[i][k-1]*dp[k+1][j]+num[k]>dp[i][j]){
              dp[i][j]=dp[i][k-1]*dp[k+1][j]+num[k];
              tree[i][j]=k; //用于输出
      }
cout<<dp[1][n]<<endl;
  • 输出
    这道题输出也是个坑
void print(int l,int r){
    if(l>r) return ;
    if(l==r){
        cout<<l<<" ";
        return ;
    }
    cout<<tree[l][r]<<" ";
    print(l,tree[l][r]-1);
    print(tree[l][r]+1,r);
}

例5

SCOI2003 字符串折叠

折叠的定义如下:

一个字符串可以看成它自身的折叠。记作\(S = S\)
\(X(S)\)\(X(X>1)\)\(S\)连接在一起的串的折叠。记作\(X(S) = SSSS…S(X\)\(S)\)

如果\(A = A',B = B'\),则\(AB = A'B'\)例如,因为\(3(A) = AAA, 2(B) = BB\),所以\(3(A)C2(B) = AAACBB\),而\(2(3(A)C)2(B) = AAACAAACBB\)

给一个字符串,求它的最短折叠。例如\(AAAAAAAAAABABABCCD\)的最短折叠为:\(9(A)3(AB)CCD\)

日常懵逼,不会

区间DP就这么结束了


接下来是背包数位DP

背包

下午

因篇幅原因\(+\)过于基础,在这里我们跳过所有背包模板

例1

\(n\)个人参加拔河比赛,要把他们分为两组,每人的实力为\(a_i\),一组的实力为这组人的实力之和。求两队实力差的最小值。(两队的人数没有限制)

题解:

\(\frac{\sum_{i=1}^{n}a[i]}{2}\)为背包容量,跑一遍阉割版的01背包即可

例2

Luogu P1782 旅行商的背包

你有一个背包容积为\(V\),有\(n\)种不可分割的物品(每种\(p_i\)个),每件的体积为\(v_i\)和价值\(c_i\),你还有\(m\)种神奇的物品,它们的价值\(c_i=a*v^2+b*v+c\)\(v\)是你决定的这件物体体积(大于等于0)。求最优价值。
\(V<=1000,n<=1000,p_i<=1000,m<=5\)

题解:

两种背包分开跑,先跑神奇的物品,再用跑完的dp数组去跑多重背包

例3

Vijos P1240 朴素的网络游戏

有一家旅馆,有\(n\)间房间,每间可以住\(a_i\)人,需要\(b_i\)元。
\(i\)个男人,\(j\)个女人来住宿,其中有\(k\)对夫妻,要求每间房间住的全是同性或者是一对夫妻(单人间无法住夫妻)。
问最少的总租金。
\(n,i,j<=300\)

题解:

一看到这道题,机房里就充满了快♂活的气息

首先,你需要想到:存在一种最优方案使得之多有一对夫妻在一件房间内。因为如果有两对,使两个男性住一间,两个女性住一间。

所以这道题里,我们只要考虑有一对夫妻就可以了

  • 定义状态
    \(dp[i][j][k][0/1]\)表示前\(i\)个房间里住了\(j\)个男性、\(k\)个女性、有没有夫妻

  • 状态转移
    \(dp[i][j][k][0]=min(dp[i-1][j][k][0],dp[i-1][j-a[i]][k][0]+b[i],dp[i-1][j][k-a[i]][0]+b[i])\)
    \(dp[i][j][k][1]=min(dp[i-1][j][k][1],dp[i-1][j-1][k-1][0]+b[i],\) \(dp[i-1][j-a[i]][k][1]+b[i],dp[i-1][j][k-a[i]][1]+b[i])\)

例4

HAOI2012 音量调节

开始有一个数\(begin\),给一个长为\(n\)的序列\(c_i\),每次操作可以选择把开始的数加或减\(c_i\),变为新的数,之后在上一次的数的基础上加或减。要求每次操作之后的数要大于等于0,小于等于\(max\),求最后一次操作之后这个数的最大值。如果没有满足要求的解输出-1.
\(0 {<=} begin {<=} max {<=} 1000,1{<=}n{<=}50\)

题解:

  • 定义状态
    \(dp[i][j]\)表示\(i\)次操作后,数为\(j\)是否可行

  • 状态转移
    \(if\;\;dp[i][j]{==}1\)
    \(dp[i+1][j+c[i]]=1(j+c[i]<=max)\)
    \(dp[i+1][j-c[i]]=1(j-c[i]>=0)\)

例5

Bzoj 2287 消失之物

\(n\)件物品,每件物品有体积\(v_i\),问装满体积\(V\)的方案数。答案对10取模。
但是你要输出:如果第i件物品消失了,装了体积为j的方案数。\(i=1...n,j=1...V\)
\(n,V<=1000\)

嗯,不会


数位

感觉数位DP有些迷,真心没怎么听懂

例1

51nod 1009 数字1的数量

给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
例如:n = 12,包含了5个1。1,10,12共包含3个1,11包含2个1,总共5个1。

题解:

  • 首先可以预处理出来如果后\(i\)位数字随便选,那么一共有多少个\(1\),记为\(f[i]\)\(f[i]=i\times 10^{i-1}\)
    分别计算如果前\(i\)位和\(n\)的前\(i\)位恰好相同,那么有多少个\(1\).

  • 如:
    \(n=124056\) 第一位为0~1
    考虑第一位为0时,那么之后的位可以随便选,对答案贡献\(f[5]\),而这一位的0不贡献答案
    第一位为1时,那么之后的为不能随便选,只有\(24057\)种选法。这一位对答案的贡献是\(24057\),之后继续计算后五位对答案的贡献
    此时第一位固定为\(1\)。第二位可以是\(0/1/2\)
    是0时,第2位不贡献答案,但是后面4位随便选。贡献\(f[4]\)
    是1时,第2位贡献答案为\(10^4\)。后面4位随便选,贡献\(f[4]\)
    是2时,第2位不贡献答案,后面4位不能随便选,答案不能直接计算,继续固定第二位,看第3位的数值
    ......
    直到最后一位。

例2

Hdu 3652 B-number

找出1~n范围内含有13并且能被13整除的数字的个数
\(n<=10^{17}\)

我太菜了,van♂全不会

背包和数位就到此结束了


Day 3

接下来让我们进入状压DP

上午

例1

Luogu P2622 关灯问题II

现有\(n\)盏灯,以及\(m\)个按钮。每个按钮可以同时控制这\(n\)盏灯——按下了第\(i\)个按钮,对于所有的灯都有一个效果。按下\(i\)按钮对于第\(j\)盏灯,是下面3中效果之一:如果\(a[i][j]\)\(1\),那么当这盏灯开了的时候,把它关上;如果为\(-1\)的话,如果这盏灯是关的,那么把它打开;如果是\(0\),则无效果。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。

题解:

  • emmmmm,这题只状压,不DP

  • 存状态的时候状压

  • 然后将能互相到达的状态之间连边,然后广搜最短路就好了

例2

SCOI2005 互不侵犯

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上、下、左、右,以及左上、左下、右上、右下八个方向上附近的各一个格子,共8个格子。
\(n<=9,k<=n^2\)

题解:

  • 定义状态
    肯定要状压
    \(dp[i][k][j]\)表示前\(i\)行放了\(k\)个国王,j表示第\(i\)行的摆放方式

  • 状态转移
    我们要快速的判断摆放方式是否合法
    \(i\)表示某一行的状态
  • 同一行
    \(i\And (i { > > }1)\)
  • 相邻行的状态\(k\)
    \(i\And k\)
    \(i\And (k{ > > }1)\)
    \(i\And (k{ < < }1)\)

以上的式子中如果有一个结果为1,说明无法转移
\(get[i]\)表示数字\(i\)的二进制位中\(1\)的数量,也就是第\(i\)行的国王数量,\(l\)表示\(i-1\)行国王的状态
\(dp[i][k][j]+=dp[i-1][k-get[i]][l]\)

状压完结散花


欢迎来到DP优化

下午

除了例1,别的啥都不会,只会暴力DP……什么矩阵加速、数据结构维护,不存在的。

例1

斐波那契数列
\(f[1]=f[2]=1\)
\(f[n]=f[n-1]+f[n-2](n >= 3)\)
\(f[n]\mod(10^9+7)\)
\(n<=10^{18}\)

题解:

矩阵快速幂加速,不解释

例2

Codeforces 821E Okabe and El Psy Kongroo

你在\((0,0)\)。在\((x,y)\)时,每次移动可以到达\((x+1,y+1),(x+1,y),(x+1,y-1)\),平面上有\(n\)条线段,平行于\(x\)轴,参数为\(a_i,b_i,c_i\),表示在\((a_i,c_i)\)\((b_i,c_i)\)的一条线段,保证\(b[i]=a[i+1]\),要求你一直在线段的下方且在\(x\)轴上方,即\(a_i {<= } x { <=} b_i\)时,\(0 { <= }y{ <= }c_i\)。问到达\((k,0)\)的方案数,对\(10^9+7\)取模。
\(n<=100,k<=10^{18},ci<=15\)

题解:

实现不是很会,但明白了做法,思路确实很神奇

直接DP就是:\(dp[i][j]=dp[i-1][j]+dp[i-1][j-1]+dp[i+1][j-1]\)

但是肯定会T,所以我们可以用矩阵来加速

\(\begin{bmatrix} 1 & 0 & 0 & 0\end{bmatrix}\) \(\begin{bmatrix} 1 & 1 & 0 & 0 \\ 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 \\ 0 & 0 & 1 & 1\end{bmatrix}\)

这样应该就可以了吧?(超级不自信)

例3

Openjudge Noi 9277 Logs Stacking

给出在最底层的木头的个数,问有多少种堆放木头的方式,当然你的堆放方式不能让木头掉下来.
在堆放的时候木头必须互相挨着在一起.
\(n <= 200000\)

题解:

  • 正常解法
    \(dp[i]=dp[i]+s[i]\)
    其中s是dp的前缀和。

  • 非正常解法
    找规律 2333

例4

Hdu 4362 Dragon Ball

在连续的\(n\)秒中,在\(x\)轴上每秒会出现\(m\)个龙珠,出现之后会立即消失,知道了第一秒所在的位置,每从一个位置移动到另一个位置的时候,消耗的价值为\(abs(i-j)\), 拿到龙珠也要消耗一个价值(不同龙珠的价值不同),问\(n\)秒之后最少消耗多少价值。
\(m <= 500,n<=1000\)

题解:

虽然老师在上面讲+\(\mathfrak{Lancelot}\) dalao给我私下讲,但我还是有些迷,我太蒟了怎么破QAQ

  • 暴力DP
    \(dp[i][j]\)表示在\(i\)秒后,在第\(j\)个龙珠的位置上的最小代价。
    \(dp[i][j]=min(dp[i-1][k]+abs(pos[i-1][k]-pos[i][j]))(k=1..m)+cost[i][j]\)
    时间复杂度\(O(m^2n)\)

  • 优化DP
    把绝对值拆开,变成向左走和向右走两种。
    把当前时间的龙珠按位置排序,从左到右扫描,维护一个最小的\(s=dp[i-1][k]-pos[i-1][k]\),这样\(dp[i][j]=s+pos[i][j]+cost[i][j]\)
    从右到左类似。
    \(O(mnlogm)\)

例5

Hdu 5009 Paint Pearls

给你一个数组,每个值代表一种颜色,每次选一个区间删去,代价是区间内颜色种类数的平方,删完所有数组,问你最小代价是多少。
\(n<=50000\)

题解:

没怎么听因为听了也听不懂

但是应该是双向链表+DP。DP转移时直接从上一个最后一次出现的颜色那转移过来就可以。另外,区间的长度最多为\(\sqrt n\)个,也可以剪枝
时间复杂度\(O(n\sqrt{n})\)

DP优化也就这样了


嗯,树上DP了解一下

Day 4

上午

例1

Luogu P1352 没有上司的舞会

某大学有\(N\)个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数\(R_i\),但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
\(n<=6000\)

题解:

树上DP经典题目

  • 定义状态
    \(dp[i][0/1]\)表示节点i不被选/被选所获得的最大快乐指数

  • 边界条件
    \(dp[leaf][1]=a[i]\)
    \(leaf\)表示叶子结点,\(a[i]\)是原序列

  • 状态转移
    \(dp[from][0]+=max(dp[to][1],dp[to][0])\)
    \(dp[from][1]+=dp[to][0]+a[from]\)

例2

树的直径

给一棵树,求树上最长路径的长度。
\(n<=500000\)

题解:

  • 解法一:
  • DP
    考虑树上dp,确定一个根,一条路径一定存在一个深度最浅的节点。枚举每个点成为lca,看向子树伸出去的两条路径的长度和最长是多少。
    \(dp[i][0/1]\)表示从i号点向下的最长/次长路径长度。
    \(dp[i][0]=max(dp[son][0])+1\)注意只能用每个子树的最长路径更新i的最长和次长路径。即使一个子树次长路径很大,也不能更新,否则lca不是i。
    \(O(n)\)

  • 解法二:
  • 随便找一个点,用\(\mathcal{dfs}\)找到离这个点最远的点\(i\),再用\(\mathcal{dfs}\)找离\(i\)最远的点\(j\)\(i\)\(j\)的路径是一条直径。
    证明正确性吗?不存在的,我懒得打了(逃。
    P.S. 法二不能用于负边权

例3

Luogu P2014 选课

\(n\)节课可以选,每节课有至多一个前置课程,和这节课的学分,问如果只能选\(m\)节课,最多有多少学分。
\(n<=300\)

题解:

  • 我说这题是树上跑背包你信么?

  • 算法主体
int dp[310][310],a[310],n,m; //dp[i][j]表示a[ ]
void dfs(int f){
    dp[f][1]=a[f];
    for(int i=head[f];i!=-1;i=tree[i].nex){
        int to=tree[i].t; dfs(to);
        for(int j=m;j>=1;j--){
            for(int o=j-1;o>=1;o--)
              dp[f][j]=max(dp[f][j],dp[f][j-o]+dp[to][o]);
        }
    }
}

例4

Hdu 4079 Terrorist’s destroy

给定一颗树,每条边有一个权值w,问切掉哪条边之后,分成的两颗树的较大的直径*切掉边的权值最小?如果存在多条边使得结果相同,输出边id最小的。
\(n<=100000\)

题解:

我们要分类讨论一下:

  • 要切的边在不在直径上
    我们直接用那条边的权值去乘直径就可以了

  • 要切的边在直径上
    这时就需要我们能够快速的算出切开后两部分的的直径
    要解决这个问题,我们要先算出图的一条直径,记下起点\(s\)和终点\(t\),然后分别以\(s\)\(t\)为根,去找直径,处理出来的\(dp[u]\)就是\(fa[u]->u\)被切开后的直径

例4

Codeforces 219D Choosing Capital for Treeland

给一棵树,每条边有方向,改变一条边方向的代价是1.
对于一个点,如果选它为根,那么需要把方向不对的边改变方向(都变成深度小的点指向深度大的点)。
问选一个点为根的最小代价。和选哪些点的代价是这个数字。
\(n<=200000\)

题解:

\(dp[u]\)表示以\(u\)为根要调整的次数,再记录下方向
一遍DFS,同向加,反向减

例5

ZJOI2008 骑士

给一个环套树森林,求最大权独立集。(就是相邻的点不能同时选)
\(n<=1000000\)

题解:

先找到那个环。随便找两个点,暴力枚举这两个点选不选。之后变成森林。

这是老师PPT上的题解,就一句话,十分简练。

嗯,然后我就懵逼了……

树型DP就这么在懵逼中结束了,接下来有更懵逼的……


期望DP是什么?能吃吗?斜率优化又是什么?

讲真,下午彻底懵逼

下午

例1

Zoj 3551 Bloodsucker

开始有一个吸血鬼,n-1个平民百姓。每天等概率选出两个人,如果是一个吸血鬼一个平民,平民以p的概率被转化为吸血鬼,否则什么也不发生。问每个人都变成吸血鬼的天数期望。
\(n<=100000\)

题解:

\(dp[i]\)为有\(i\)个吸血鬼的话还期望需要多少天完成。
\(p[i]\)为此时转换了一个平民的概率。
\(p[i]=\frac{2\times (n-i)\times i}{n\times(n-1)}\times p\)
\(dp[i]=p[i]\times dp[i+1]+(1-p[i])\times(dp[i])+1\)

例2

Poj 3744 Scout YYF I

\(x\)轴上,你现在的起点在\(1\)处。在\(N\)个点处布有地雷,\(1<=N<=10\)。地雷点的坐标范围:\([1,100000000]\).
每次前进\(p\)的概率前进\(1\)步,\(1-p\)的概率前进\(2\)步。问顺利通过这条路的概率。就是不要走到有地雷的地方。

题解:

  • \(dp[i]\)表示一条路径经过i的概率。
    \(dp[1]=1,dp[i]=p\times dp[i-1]+(1-p)\times dp[i-2]\)
    快速幂算出\(1-dp[x_1]\)就是安全通过第一个地雷的概率,此时在\(x_1+1\),你又要通过\(x_2,...x_n\),就是把\((x_1+1)\text{~}x_2\)再做一次,全都安全通过的概率乘起来。

  • 嗯,以上全都是抄的老师的PPT

例3

hdu 2262 Where is the canteen

现在在一个\(n\times m\)规模的区域上从‘@’处出发,每次都随机向前后左右四个方向中选择可以走的方向进入('#‘不可走, 不能越过边界)。现在问到达终点\(’\$'\)的期望步数, 终点可能有多个, 输入保证一定有起点\(n,m<=15\), 如果无法到达任何一个终点输出\(-1\)

题解:

高斯消元,爱信不信。

例4

hdu 3507 Print Article

给一个数列ai,要求划分成若干段,一段的代价是sigma(ai)^2+M,总的代价是每段代价和。
求最小总代价。
\(n<=500000\)

题解:

  • 嗯,真·斜率优化
  • 嗯,真·不会
  • 嗯,还是把老师的代码放上来吧

膜拜一下老师 orz

#include <iostream>
#include <stdio.h>

using namespace std;

int n , m , sum[500010] , q[500010] , dp[500010] , head , tail;
int getup ( int x ) {
    return dp[x] + sum[x]*sum[x];
}
int getdown ( int x ) {
    return 2*sum[x];
}
void work () {
    int i;
    for ( i = 1 ; i <= n ; i++ ) scanf ( "%d" , &sum[i] );
    sum[0] = dp[0] = 0;
    for ( i = 1 ; i <= n ; i++ ) sum[i] += sum[i-1];
    q[1] = 0; head = tail = 1;
    for ( i = 1 ; i <= n ; i++ ) {
        //斜率优化
        while ( head < tail && getup(q[head+1]) - getup(q[head]) <= sum[i]*(getdown(q[head+1])-getdown(q[head])) ) head++;
        dp[i] = dp[q[head]] + ( sum[i] - sum[q[head]] ) * ( sum[i] - sum[q[head]] ) + m;
        while ( head < tail && ( getup(q[tail]) - getup(q[tail-1])) * ( getdown(i) - getdown(q[tail]) ) >=
                                            ( getup(i) - getup(q[tail]) ) * ( getdown(q[tail]) - getdown(q[tail-1]) ) ) tail--;
        q[++tail] = i;
    }
    printf ( "%d\n" , dp[n] );
}
int main () {
    while ( scanf ( "%d%d" , &n , &m ) != EOF ) work ();
    return 0;
}

至此,DP应该就告一段落了。

我DP掌握的还是不够好,以后也要多加锻炼。

转载于:https://www.cnblogs.com/wxl-Ezio/p/9337723.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 陆家嘴学堂邹博所提供的python机器学习与深度学习课件内容丰富,涵盖了机器学习和深度学习的基础知识和应用案例。课件主要包括以下几个方面的内容: 首先,课件介绍了机器学习和深度学习的基本概念和原理。从机器学习的监督学习、无监督学习到深度学习的神经网络结构、反向传播等基础知识进行了详细讲解,并结合实际的应用场景进行了案例分析。 其次,课件涵盖了机器学习和深度学习的常用算法和模型。通过讲解决策树、支持向量机、逻辑回归、卷积神经网络、循环神经网络等常用的算法和模型,使学员能够理解不同算法的原理和适用场景,并能够在实际项目中进行算法选择和调优。 此外,课件还介绍了机器学习和深度学习的数据预处理和特征工程方法。学员将学习如何对各种类型的数据进行清洗、归一化和编码等预处理操作,以及如何通过特征选择、降维和构建新特征等技术来提取有用的特征。 最后,课件还包含了使用Python进行机器学习和深度学习的实践案例。通过使用Python中常用的机器学习和深度学习库(如Scikit-learn、TensorFlow、Keras等),学员将学习如何完成从数据处理到模型构建和评估的完整机器学习和深度学习流程。 总之,陆家嘴学堂邹博的python机器学习与深度学习课件内容丰富,不仅能够帮助学员建立起对机器学习和深度学习的基本理论和算法的理解,还能够通过实际案例的演示帮助学员掌握Python在机器学习和深度学习中的应用技巧。 ### 回答2: 陆家嘴学堂邹博编写的Python机器学习与深度学习课件是一套全面且实用的学习资料。该课件将机器学习和深度学习两个领域有机地结合在一起,帮助学习者理解和应用这两个领域的重要概念和算法。 首先,课件从机器学习的基本概念出发,包括监督学习、无监督学习和增强学习等。它详细介绍了机器学习的主要算法,如回归分析、决策树、支持向量机、朴素贝叶斯等,并通过代码案例演示了这些算法在实际问题中的应用。 其次,这份课件还深入讲解了深度学习的原理和应用。它介绍了神经网络的基本结构和训练方法,包括前向传播、反向传播和优化算法等。此外,课件还介绍了深度学习中常用的网络结构,如卷积神经网络、循环神经网络和生成对抗网络等,并通过一系列实例展示了它们在图像识别、自然语言处理和生成模型等方面的应用。 最后,这份课件还为学习者提供了大量的实战项目和练习题,以帮助巩固所学内容。通过实际操作,学习者可以更深入地理解算法的原理和实现过程,并能够将其应用到真实的数据集中。 总的来说,陆家嘴学堂邹博编写的Python机器学习与深度学习课件是一份非常有价值的学习资料。无论是初学者还是已经有一定机器学习基础的学习者,都可以通过这份课件系统地学习和掌握Python机器学习与深度学习的知识和技能。 ### 回答3: 陆家嘴学堂邹博编写的《python机器学习与深度学习课件》是一本基于Python编程语言的机器学习和深度学习教材。这本课件首先介绍了机器学习和深度学习的基本概念和原理,然后逐步介绍了Python在这两个领域的应用。 课件的第一部分主要讲解了机器学习的基本理论和常用算法,包括监督学习、无监督学习和强化学习等。通过清晰的示例和实际的案例,读者可以了解到如何使用Python编写机器学习算法,并应用到实际问题中。 而在第二部分,课件重点讲解了深度学习,包括神经网络、卷积神经网络和循环神经网络等。课件详细介绍了这些算法的原理和实现方法,以及它们在计算机视觉、自然语言处理和推荐系统等领域的应用。通过学习这些章节,读者可以全面了解深度学习的概念和技术,并能够使用Python编写深度学习模型。 此外,课件也提供了丰富的实际应用案例,例如图像分类、文本生成和推荐系统等,这些实例将帮助读者将所学知识应用到实际问题中,并加深对机器学习和深度学习的理解。 总的来说,陆家嘴学堂邹博编写的《python机器学习与深度学习课件》是一本系统而且易懂的教材,对于想要学习Python机器学习和深度学习的读者来说是一本非常有价值的资源。无论是对于初学者还是有一定基础的人来说,这本课件都能帮助他们在机器学习和深度学习的领域取得进一步的理论和实践能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值