(1)区间DP
基本模板
for(int len=2;len<=n;len++)
{
for(int i=1;i<n;i++)
{
int j=i+len-1;
if(j>n) break;
for(int k=i;k<=j;k++)
{
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
}
(2)概率/期望dp
看缘分…
(3)状压dp
用二进制的0/1表示状态,通过遍历检查状态合法性来进行状态转移,达到最终目的
bool check(int a)
{
/*
通过对0的间距或者1的间距等条件进行判断,合法再进行状态转移
对于很多题目,我们还可以在初始时进行check,
然后将所有可行状态储存起来,
这样在最后写dp转移方程时可以节约大量的时间
*/
}
(4)数位dp
一般用来处理区间内有多少个数字满足条件
基本套路模板
int dfs(int pos,int states,int lead,int limit)
/*
limit传输的是这一位的数字是否为上下界
lead代表前导0,部分题目不需要处理
states表状态,状态数目不够可以继续增加
*/
{
if(pos==-1)
{
if(/*满足条件*/) return 1;
else return 0;
}
if(!limit&&!lead&&dp[pos][states]!=-1) return dp[pos][states];
/*
相同数位且相同状态时
*/
int up=limit?num[pos]:9;//最高位为多少
int ans=0;
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,states,lead&&i==0,limit&&num[pos]==i);
}
if(!limit&&!lead) dp[pos][states][nm]=ans;
return ans;
}
int solve(int a)
{
cnt=0;
while(a)
{
num[cnt++]=a%10;
a/=10;
/*
这里可以根据需要的进制来进行改变
*/
}
memset(dp,-1,sizeof(dp));
return dfs(cnt-1,0,1,0,1);
}
signed main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
printf("%d\n",solve(m)-solve(n-1));
}
return 0;
}
(5)单调队列优化dp
首先推导出状态转移方程,
若转移方程中有一维实质上是单调增/减,
则可以把其用单调队列给优化掉
单调队列基本模板
deque<node> q;
for(int i=1;i<=m;i++)
{
while(!q.empty()&&q.back().val>a[i])
{
q.pop_back();
}
if(!q.empty()&&i-q.front().i-id>m)
{
q.pop_front();
}
q.push_back({i,a[i]});
ans[i]=q.front().val;
}
(6)树状dp
对一颗树进行dfs,dfs后进行状态转移
void dfs(int u,int father)
{
for(int i=0;i<v[u].size();i++)
{
int tmp=v[u][i];
if(tmp==father) continue;
dfs(tmp,u);
/*在这里状态更新*/
}
return;
}