【7*】期望DP学习笔记
前言
由于马上就要把同学的《概率论与数理统计》还回去了,所以赶快看一点,并做一点笔记。
感觉网上很多文章讲期望 DP 都讲得不够透彻啊,就写一些自己的理解造福后人吧,自我感觉讲得很透彻。
此类知识点大纲中并未直接涉及,所以【7】是我自己的估计,后带星号表示估计,仅供参考。
基础定义
在概率论中,一个随机试验具有如下三个特征。
\(1\):可以在相同的条件下重复地进行。
\(2\):每一次试验的结果不止一个。并且能实现验明试验所有可能的结果。
\(3\):进行试验前不能确定哪一个结果会出现。
我们把一个随机试验 \(E\) 的所有可能的结果组成的集合称为随机试验 \(E\) 的样本空间,记作 \(S\)。样本空间的每一个元素,称为样本点。
一般的,我们称随机试验 \(E\) 的样本空间 \(S\) 的子集称为 \(E\) 的随机事件,简称事件。特别的,对于只有一个样本点的事件,称为基本事件。
事件运算即为基本的集合运算,特别的,对于两个事件 \(A\) 和 \(B\),我们把 \(A\cap B\) 简记为 \(AB\),其意义为 \(A\) 和 \(B\) 一起发生。
概率
概率的基本定义
设 \(E\) 是随机试验,\(S\) 是它的样本空间,对于 \(E\) 的每一事件赋予一个实数 \(P(A)\),称为 \(A\) 的概率。这个函数满足如下条件。
\(1\):\(P(A)\ge 0\)
\(2\):\(P(S)=1\)
\(3\):设 \(A_1,A_2\dots\) 是一系列互不相容的事件,即对于 \(A_iA_j=\varnothing,i\ne j,i,j\in\{1,2\dots\}\),有:
\]
下面是一些常用的推论。
\(1\):\(P(\varnothing)=0,P(A)\le 1\)
\(2\):(有限可加性)设 \(A_1,A_2\dots A_n\) 是一系列互不相容的事件,即对于 \(A_iA_j=\varnothing,i\ne j,i,j\in\{1,2\dots n\}\),有:
\]
\(3\):(逆事件的概率)\(P(\bar A)=1-P(A)\)
\(4\):(加法公式)类似容斥原理。
\]
样本空间中具有有限种元素,且每一种事件发生概率相同的概率模型称为古典概型。在此概率模型下,可以得到概率的计算式。
\]
条件概率
事件 \(A\) 已发生的情况下事件 \(B\) 发生的概率,称为条件概率,记作 \(P(B\mid A)\)。
在古典概型下,设试验的基本事件总数为 \(n\),\(A\) 所包含的基本事件数为 \(m(m\gt0)\),\(AB\) 所包含的基本事件数为 \(k(k\gt0)\),不难推出条件概率的计算公式。
\]
不难发现,\(P(A)\) 其实等价于 \(P(A\mid S)\)。
注意到条件概率 \(P(X\mid A)\) 满足概率的基本定义,其中 \(X\) 为 \(A\) 的任意子集。于是,概率 \(P(X)\) 的推论对条件概率 \(P(X\mid A)\) 也适用。以加法原理的特例举个例子。
\]
逆用条件概率的计算式,可以得到概率论中的乘法定理。
\]
这个式子很容易推广到多个事件的情况。
\]
\]
上面几个公式可以实现条件概率与与积事件概率之间的互化。
特别的,如果 \(P(B\mid A)=P(B)\),这时乘法公式可以改写为如下形式。
\]
如果 \(A\) 和 \(B\) 满足上面式子,那么我们称 \(A\) 与 \(B\) 是独立的。这其实就是我们平常认为的概率计算公式。
划分
设 \(S\) 为试验 \(E\) 的样本空间,\(B_1,B_2\dots B_n\) 为 \(E\) 的一组事件。若 \(B_1,B_2\dots B_n\) 满足如下限制,则称 \(B_1,B_2\dots B_n\) 是 \(S\) 的一组划分。
\(1\):\(B_iB_j=\varnothing,i\ne j,i,j\in\{1,2,\dots,n\}\)
\(2\):\(B_1\cup B_2\cup\dots\cup B_n=S\)
对于一个划分,我们有全概率公式。
\]
下面给出证明。
由划分的定义,不难得出如下式子。
\]
套进有限可加性的式子,再使用乘法公式展开,就证完了。
P(A)&=P(AB_1)+P(AB_2)+\dots+P(AB_n) \\
&=P(A\mid B_1)P(B_1)+P(A\mid B_2)P(B_2)+\dots+P(A\mid B_n)P(B_n)
\end{aligned}\]
事实上,在实际问题中,\(P(A)\) 不能直接求得,但却能容易找到 \(S\) 一个划分 \(B_1,B_2\dots B_n\),使得 \(P(B_i)\) 和 \(P(A\mid B_i)\) 都容易求得。这时我们就可以通过全概率公式求出 \(P(A)\)。
特别的,如果一个划分 \(n=2\),记 \(B_1\) 为 \(B\),则 \(B_2\) 就是 \(\bar B\),此时有全概率公式的特殊形式。
\]
对于一个划分,我们还有一个重要的公式,贝叶斯公式。
\]
下面给出证明。
我们使用条件概率的定义展开。
\]
把上面使用乘法定理展开,下面用全概率公式展开,就得到了贝叶斯公式。
\]
特别的,如果一个划分 \(n=2\),记 \(B_1\) 为 \(B\),则 \(B_2\) 就是 \(\bar B\),此时也有贝叶斯公式的特殊形式。
\]
期望
对于一个随机试验 \(E\),我们可以将样本空间 \(S\) 中的每一个时间 \(e\) 对应到一个实数 \(X(e)\),我们称 \(X=X(e)\) 为随机变量。
设离散型随机变量的分布为 \(P(X=x_k)=p_k,k=1,2\dots\),我们将如下式子定义为随机变量 \(X\) 的期望。
\]
实际上,如果上面是式子不是绝对收敛,这个式子不能被称为 \(X\) 的期望。但是 OI 中的式子一般都是绝对收敛,所以我们不考虑这种情况。
现在,假设我们知道了 \(P(X=x_k)=p_k,k=1,2\dots\) 的期望,我们想知道 \(P(X'=x_k+1)=p_k,k=1,2\dots\) 的期望,我们考虑如何求解。
\]
由此,我们引出期望的线性性。这对于期望 DP 来说是重要的基本功。现在,我们记 \(E(X)\) 表示随机变量 \(X\) 的期望,那么我们有如下式子。
\(1\):\(c\) 为常数,\(E(c)=c\)
\(2\):\(E(X+Y)=E(X)+E(Y)\)
\(3\):若 \(X,Y\) 相互独立,\(E(XY)=E(X)E(Y)\)
\(4\):\(a,b\) 为常数,\(E(aX+b)=E(aX)+b\)
上述式子均只需要展开期望的定义即可证明,在此不做赘述。
在期望 DP 中,状态 \(f[x]\) 表示状态 \(x\) 下随机变量的期望。也就是说,在期望 DP 中,\(f[x]\) 实际上是一个随机变量 \(X\) 的期望。有了这一点认识,我们就可以利用期望的展开式和期望的线性性来推式子。
另外,期望 DP 一般使用逆推的形式,也就是记录 \(f[x]\) 表示到终末状态的期望。因为如果顺推,从初始状态 \(A\) 到达当前状态 \(B\) 的概率并不是 \(P(AB)\),而是 \(P(B\mid A)\)。而我们要求的期望是在 \(P(AB)\) 意义下的。此时如果逆推,注意到终末状态 \(A\) 的必然的,于是就有 \(P(AB)=P(B\mid A)P(A)=P(B\mid A)\),不会产生矛盾。
因此,我们不难得知,如果终止状态确定(即所有终止状态都被包含在状态中)执行逆推,起始状态确定执行顺推。
期望 DP 有如下经典模型,这里以逆推为例。
\]
其中 \(\text{nxt}(i)\) 表示 \(i\) 的后继状态,\(w(i,j)\) 表示从 \(i\) 到 \(j\) 对答案的贡献,\(p(i,j)\) 表示从 \(i\) 转移到 \(j\) 的概率。
这个模型是容易证明的。我们只需要展开期望的定义式就能证明。
f[i]&=\sum_{x}p_xx\\
&=\sum_{j\in\text{nxt}(i)}\sum_{x'}p'_{x'}\times p(i,j)\times (x'+w(i,j))\\
&=\sum_{j\in\text{nxt}(i)}p(i,j)(\sum_{x'} p'_{x'}x'+ \sum_{x'}p'_{x'}w(i,j))\\
&=\sum_{j\in\text{nxt}(i)}p(i,j)\times(f[j]+w(i,j))
\end{aligned}\]
例题
例题 \(1\) :
CF453A Little Pony and Expected Maximum
求期望,直接展开期望定义式。
\]
考虑求 \(P(\max\{x_i\}=k)\),使用前缀和技巧。
P(\max\{x_i\}=k)&=P(\max\{x_i\}\le k)-P(\max\{x_i\}\le k-1) \\
&=(\frac{k}{m})^n-(\frac{k-1}{m})^n
\end{aligned}\]
带回原式,不难发现是一个类似分数裂项的形式,进一步展开得到最终的计算式。
E(\max\{x_i\})&=\sum_{k=1}^m(\frac{k}{m})^nk-(\frac{k-1}{m})^nk \\
&=m-\sum_{k=1}^{m-1}(\frac{k}{m})^n
\end{aligned}\]
#include <bits/stdc++.h>
using namespace std;
long long m,n;
long double power(long double a,long long p)
{
long double x=a,ans=1;
while(p)
{
if(p&1)ans=ans*x;
p>>=1;
x=x*x;
}
return ans;
}
int main()
{
scanf("%lld%lld",&m,&n);
long double ans=m;
for(int i=1;i<=m-1;i++)ans-=power((long double)i/m,n);
printf("%Lf\n",ans);
return 0;
}
例题 \(2\) :
方案的总数是容易计算的,为 \(\prod_{i=1}^na_i\),那期望就是统计每一种方案的贡献和再除掉。
转化贡献体。考虑每一种方案显然不可做,考虑每一个位置会贡献 \(i\) 多少次。首先位置 \(i-1\) 和 \(i\) 的答案必须相同,有 \(\min(a_{i-1},a_i)\) 种选法。其余位置随便选,有 \(\frac{\prod_{i=1}^na_i}{a_{i-1}a_i}\) 种选法。把除方案总数的 \(\prod_{i=1}^na_i\) 除进来,得到每个位置的贡献为 \(\frac{\min(a_{i-1},a_i)}{a_{i-1}a_i}\),累加即可。
注意题中是一个环,需要特判。
#include <bits/stdc++.h>
using namespace std;
long long n,x,y,z,a[20000000];
double ans=0;
int main()
{
scanf("%lld%lld%lld%lld%lld",&n,&x,&y,&z,&a[1]);
for(int i=2;i<=n;i++)a[i]=(a[i-1]*x+y)%100000001;
for(int i=1;i<=n;i++)a[i]=a[i]%z+1;
for(int i=2;i<=n;i++)ans+=(double)min(a[i],a[i-1])/a[i]/a[i-1];
ans+=(double)min(a[1],a[n])/a[1]/a[n];
printf("%.3lf",ans);
return 0;
}
例题 \(3\) :
由于终点状态是确定的,考虑设计状态 \(f[i]\) 表示从 \(i\) 走到 \(n\) 期望需要多少步。记 \(d[i]\) 为点 \(i\) 的出度,套用经典模型得到转移式。其中 \(\text{dis}\) 为边权。
\]
拓扑排序直接转移即可。
#include <bits/stdc++.h>
using namespace std;
struct edge
{
long long v,d,nxt;
}e[300000];
long long n,m,u,v,w,h[300000],d[300000],sd[300000],q[300000],l=1,r=0,cnt=0;
double f[300000];
void add_edge(long long u,long long v,long long w)
{
e[++cnt].nxt=h[u];
e[cnt].v=v;
e[cnt].d=w;
h[u]=cnt;
d[v]++;
}
void topo_sort(long long s)
{
l=1,r=0,q[++r]=s;
while(l<=r)
{
long long x=q[l];
for(int i=h[x];i;i=e[i].nxt)
{
f[e[i].v]+=(f[x]+e[i].d)/sd[e[i].v],d[e[i].v]--;
if(d[e[i].v]==0)q[++r]=e[i].v;
}
l++;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&u,&v,&w),sd[u]++,add_edge(v,u,w);
topo_sort(n);
printf("%.2lf",f[1]);
return 0;
}
例题 \(4\) :
AT_abc263_e [ABC263E] Sugoroku 3
记 \(f[x]\) 表示从 \(x\) 开始到 \(n\) 的期望步数,先考虑不会跳到自己的情况。套用经典模型不难得到如下式子。
\]
现在我们考虑能转移到自己,还是一样计算。
\]
把 \(f[x]\) 移到左边。
\]
化系数为 \(1\),就得到递推式。
\]
后缀和优化一下,时间复杂度 \(O(n\log V)\)。
#include <bits/stdc++.h>
using namespace std;
long long n,a[300000],f[300000],h[300000];
const long long mod=998244353;
long long power(long long a,long long p)
{
long long x=a,ans=1;
while(p)
{
if(p&1)ans=ans*x%mod;
p>>=1;
x=x*x%mod;
}
return ans;
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n-1;i++)scanf("%lld",&a[i]);
f[n]=h[n]=h[n+1]=0;
for(int i=n-1;i>=1;i--)f[i]=(h[i+1]-h[min(n+1,i+a[i]+1)]+a[i]+1+mod)%mod*power(a[i],mod-2)%mod,h[i]=(h[i+1]+f[i])%mod;
printf("%lld\n",f[1]);
return 0;
}
例题 \(5\) :
如果知道这一次和下一次的教室的位置,为了最小化消耗的体力值,肯定会选择走最短路,使用 Floyd 预处理出任意两点之间的最短路。
由于起始状态必然是最开始两间教室之一,结束状态也必然是最后两间教室之一,都是确定的,所以可以随便选择 DP 顺序。我们发现知道相邻两次有没有都提出换教室就能得到相邻两次每种情况的概率,考虑把这个设进状态。
记状态 \(f[i][j][0/1]\) 表示现在在考虑第 \(i\) 间教室,已经提出了 \(j\) 次申请,上一次有没有换教室。记 \(\text{dist}(x,y)\) 为点 \(x\) 和点 \(y\) 之间的最短路。
先考虑 \(f[i][j][0]\),这个状态有两个前驱状态,\(f[i-1][j][0]\) 和 \(f[i-1][j][1]\),我们显然会选择期望体力值少的转移。
\]
取 \(\min\) 内第一项是两次都没有换教室,显然贡献为 \(\text{dist}(c_{i-1},c_i)\)。第二项把经典模型的公式展开了,\(f[i-1][j][1]\) 的系数是所有概率累加为 \(1\),后面是每一种情况的概率乘上贡献,即经典模型中的 \(p(i,j)\times w(i,j)\)。
类似的,我们写出 \(f[i][j][1]\) 的转移式,直接展开,考虑每一项的概率及其贡献。
\]
边界是 \(f[1][0][0]=f[1][1][1]=0\),显然消耗的体力值为 \(0\)。最后的答案就是 \(f[n][i][0/1]\) 中的最小值,其中 \(1\le i\le m\)。
#include <bits/stdc++.h>
using namespace std;
long long n,m,v,l,a,b,w,c[3000],d[3000],e[400][400];
double k[3000],f[3000][3000][2];
int main()
{
scanf("%lld%lld%lld%lld",&n,&m,&v,&l);
for(int i=1;i<=n;i++)scanf("%lld",&c[i]);
for(int i=1;i<=n;i++)scanf("%lld",&d[i]);
for(int i=1;i<=n;i++)scanf("%lf",&k[i]);
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
if(i!=j)e[i][j]=1e18;
else e[i][j]=0;
for(int i=1;i<=l;i++)scanf("%lld%lld%lld",&a,&b,&w),e[a][b]=e[b][a]=min(e[a][b],w);
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=1e18;
f[1][0][0]=f[1][1][1]=0;
for(int i=2;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j][0]=min(f[i][j][0],f[i-1][j][0]+e[c[i-1]][c[i]]);
f[i][j][0]=min(f[i][j][0],f[i-1][j][1]+k[i-1]*e[d[i-1]][c[i]]+(1-k[i-1])*e[c[i-1]][c[i]]);
if(j)f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]+k[i]*e[c[i-1]][d[i]]+(1-k[i])*e[c[i-1]][c[i]]);
if(j)f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]+k[i-1]*k[i]*e[d[i-1]][d[i]]+k[i-1]*(1-k[i])*e[d[i-1]][c[i]]+(1-k[i-1])*k[i]*e[c[i-1]][d[i]]+(1-k[i-1])*(1-k[i])*e[c[i-1]][c[i]]);
}
double mi=1e18;
for(int i=0;i<=m;i++)
for(int j=0;j<2;j++)
mi=min(mi,f[n][i][j]);
printf("%.2lf",mi);
return 0;
}
例题 \(6\) :
期望具有线性性,但不具有可乘性。也就是说,\(X^3\) 的期望不等于 \(X\) 的期望的三次方。因此,原先的期望 DP 模型失效,很难套用模型进行递推,我们需要通过期望的定义重新推一遍。
记 \(f_w[x]\) 为第 \(x\) 个位置产生的 \(X^w\) 的期望,位置 \(x\) 出现 \(1\) 的概率为 \(p[x]\),位置 \(x\) 前(包括位置 \(x\))出现连续 \(k\) 个 \(1\) 的概率为 \(p_x[k]\),展开定义式得位置 \(x\) 的期望。
f_2[x]&=p[x]\sum_{k}p_{x-1}[k](k+1)^2\\
&=p[x]\sum_{k}p_{x-1}[k](k^2+2k+1)\\
&=p[x](\sum_{k}p_{x-1}[k]k^2+2\sum_{k}p_{x-1}[k]k+\sum_{k}p_{x-1}[k])\\
&=p[x](f_2[x-1]+2f_1[x-1]+1)
\end{aligned}\]
\(f_1[x]\) 也是同理,过程就不写了。
\]
记 \(f[x]\) 为前 \(x\) 个位置 \(X^3\) 的和的期望,我们还是展开期望的定义式。
f[x]&=p[x]\sum_{k}p_{x-1}[k](f[x-1]+(k+1)^3-k^3)+(1-p[x])f[x-1]\\
&=p[x]\sum_{k}p_{x-1}[k](f[x-1]+3k^2+3k+1)+(1-p[x])f[x-1]\\
&=p[x](\sum_{k}p_{x-1}[k]f[x-1]+3\sum_{k}p_{x-1}[k]k^2+3\sum_{k}p_{x-1}[k]k+1)+(1-p[x])f[x-1]\\
&=p[x](f[x-1]+3f_2[x-1]+3f_1[x-1]+1)+(1-p[x])f[x-1]\\
&=f[x-1]+p[x](3f_2[x-1]+3f_1[x-1]+1)\\
\end{aligned}\]
这个方法可以扩展到更高次。这启示我们,如果期望无法直接递推,我们考虑展开定义式。
#include <bits/stdc++.h>
using namespace std;
long long n;
double x,g1[200000],g2[200000],f[200000];
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf",&x);
g1[i]=(g1[i-1]+1)*x;
g2[i]=(g2[i-1]+2*g1[i-1]+1)*x;
f[i]=f[i-1]+(3*g2[i-1]+3*g1[i-1]+1)*x;
}
printf("%.1lf",f[n]);
return 0;
}
例题 \(7\) :
不管怎样安排边权,每条边被经过的期望次数是一样的,所以我们可以把每条边被经过的期望次数求出来,然后对于期望经过次数少的分配大边权,经过次数多的分配小边权。
但是求边的经过次数无法转移,我们考虑求点的经过次数。我们发现,一条边的经过次数就是两端点的经过次数除以对应的度数之和。注意走到点 \(n\) 就直接结束了,所以不需要计算点 \(n\)。
考虑如何求一个点的期望经过次数 \(f[x]\)。我们直接套用经典模型。\(\text{nxt}(x)\) 表示 \(x\) 相邻的节点,\(\text{deg}[x]\) 表示点 \(x\) 的度数。
\]
高斯消元求解即可,参见 【6】矩阵学习笔记。
#include <bits/stdc++.h>
using namespace std;
long long n,m,u,v,d[1200],f[1200][1200],book[1200],cnt=0;
double a[1200][1200],b[1200],ans[1200],q[240000],sum=0,eps=1e-10;
void gauss()
{
long long now=1;
double div=0;
for(int i=1;i<=n;i++)
{
long long mx=now;
for(int j=now+1;j<=n;j++)
if(fabs(a[j][i])>fabs(a[mx][i]))mx=j;
if(fabs(a[mx][i])<eps)continue;
if(now!=mx)
{
for(int j=1;j<=n;j++)swap(a[now][j],a[mx][j]);
swap(b[now],b[mx]);
}
div=a[now][i],b[now]/=div;
for(int j=1;j<=n;j++)a[now][j]/=div;
for(int j=now+1;j<=n;j++)
{
div=a[j][i],b[j]-=b[now]*div;
for(int k=i;k<=n;k++)a[j][k]-=a[now][k]*div;
}
now++;
}
ans[n]=b[n];
for(int i=n-1;i>=1;i--)
{
ans[i]=b[i];
for(int j=n;j>i;j--)ans[i]-=a[i][j]*ans[j];
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&u,&v);
f[u][v]=1,f[v][u]=1,d[u]++,d[v]++;
}
for(int i=1;i<=n;i++)
{
if(i==1)b[i]=1;
for(int j=1;j<=n-1;j++)
if(f[j][i])a[i][j]=-1.0/d[j];
a[i][i]=1;
}
gauss();
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++)
if(f[i][j])
{
q[++cnt]=ans[i]/d[i];
if(j!=n)q[cnt]+=ans[j]/d[j];
}
sort(q+1,q+cnt+1);
for(int i=1;i<=cnt;i++)sum+=q[i]*(cnt-i+1);
printf("%.3lf",sum);
return 0;
}
后记
学习期望 DP 的过程中,我觉得独立思考是很重要的。通过独立思考,我排除了网上许多不够透彻的文章的干扰,形成了自己的见解,回头看来,有一种轻舟已过万重山的快感。
希望自己能继续保持。
一夜长安冷雨 几声马蹄彷徨
烽火长燃撞破天光
只拂一拂袖 挥别了当年模样
千里江山如烟 谁不志在四方
行道水穷不曾回望
吟鞭断流水 也能断千肠
缱绻往事 何必思量
【7*】期望DP学习笔记的更多相关文章
- 数位DP学习笔记
数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...
- DP学习笔记
DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...
- 树形DP 学习笔记
树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...
- 斜率优化DP学习笔记
先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...
- 动态 DP 学习笔记
不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...
- [总结] 动态DP学习笔记
学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...
- 插头DP学习笔记——从入门到……????
我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...
- 树形$dp$学习笔记
今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...
- 斜率优化dp学习笔记 洛谷P3915[HNOI2008]玩具装箱toy
本文为原创??? 作者写这篇文章的时候刚刚初一毕业…… 如有错误请各位大佬指正 从例题入手 洛谷P3915[HNOI2008]玩具装箱toy Step0:读题 Q:暴力? 如果您学习过dp 不难推出d ...
- 状压dp学习笔记(紫例题集)
P3451旅游景点 Tourist Attractions 这个代码其实不算是正规题解的(因为我蒟蒻)是在我们的hzoj上内存限制324MIB情况下过掉的,而且经过研究感觉不太能用滚动数组,所以那这个 ...
随机推荐
- journalctl -u docker 查看日志
转载注明出处: 1. 查看 Docker 服务的最新日志(实时滚动) sudo journalctl -u docker -f -f 参数表示 跟随(follow),会持续输出最新日志(类似 tail ...
- GitLab——重置(reset)和还原(revert)
Git 命令 reset 和 revert 的区别 - 知乎 (zhihu.com) 总结: git reset --hard 9201d9b19dbf5b4ceaf90f92fd4e4019b685 ...
- 把 Java WebApi 快速转为 Mcp-Server(使用 Solon AI MCP)
solon-ai-mcp,提供了各种 mcp 相关能力,支持 java8, java11, java17, java21, java24 .是 solon-ai 项目的重要组成部分,也可以嵌入到 sp ...
- 银河麒麟系统 jenkins docker 部署 自动化打包部署git 项目
Jenkins 是一个开源的自动化服务器,主要用于实现 持续集成(CI) 和 持续交付/部署(CD),其核心作用在于通过自动化流程提升软件开发和交付的效率与质量 一.环境准备 1. 安装 Docker ...
- WPF 制作三个点从左到右过渡隐藏显示
Code: <Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/win ...
- 集合流之“anyMatch”的应用【返回boolean类型】
判断集合中是否存在"字符串",返回boolean类型 boolean isExit = allSku.stream().map(Product::getFeatureList) . ...
- SQL 月同环比 日期 T+1 自动计算
对于离线数据的分析, 数据通常是 T+1 的, 即所有数据更新都是到 "昨天". 对于这样的同环比能自动计算是很需要的. -- 以Mysql为例: -- 今天: 2022/3/1 ...
- B1009 说反话
给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串.字符串由若干单词和若干空格组成,其中单词是由英文字母(大小 ...
- PB EB ZB YB
1B字节=8bit位 1KB=2^10B 1MB=2^20B 1GB=2^30B 1TB=2^40B 1PB=2^50B(五个屁) 1EB=2^60B(六姨) 1ZB=2^70B(七个乌贼) 1YB= ...
- 适用于Ventoy和VirtualBox的WinToGo系统制作教程
由于篇幅原因,上个教程的内容太多,并且也有挺多地方不能一概而论,因此笔者决定还是将文章拆分开来,便于只需要制作WinToGo系统或LinuxToGo系统的读者 --笔者言 这篇文章适用于只使用Vent ...