dp重修
区间 dp
枚举断点型
dp 的状态表示设计为一段区间,一般为
状态转移时,一般按照
即枚举断点
P3146 [USACO16OPEN] 248 G
套路题,
イウィ
依然套路题,用
有如下转移:
特别地,如果有:
且
此时答案为
最后答案即为
区间贡献/答案统计型
状态设计一般为区间的答案,可以考虑根据已知的信息逐步递推。
Coloring Brackets
考虑状态
此时根据区间长度划分递推不太好做,可以使用记忆化搜索。
考虑当前处理区间
- 若
,有 - 若
,即形如 的情况,递归处理两个括号后,答案即为两者乘积。 - 若
,即形如 的情况,有转移 。
注意颜色判断,代码呼之欲出。
P8675 [蓝桥杯 2018 国 B] 搭积木
设计状态
首先有一个 trick,判断
然后,设计 dp 状态:
初始状态:关于
状态转移:
最终答案:
时间复杂度
观察到状态转移的式子中是一个二维区间和,使用前缀和优化。
时间复杂度
关于此题一些解释:
- 状态计算从何而来:考虑该层的基座如何覆盖
,如果基座满足 ,那么基座的每一个方案都能对当前区间的答案做贡献。 - 答案为什么是上式:考虑以第
层为顶端的情况,所有的 都能贡献答案,最后加上一个不放的情况。
dp 做题
[ABC207E] Mod i
将序列分成若干段,使得第
分析
设计出普通的 dp。
令
有以下转移:
这相当于枚举上一个断点
有边界条件
答案
这样做时间复杂度
const int mod=1e9+7;
const int maxn=3005;
int n,a[maxn],dp[maxn][maxn],pre[maxn];
int query(int l,int r)
{
return pre[r]-pre[l-1];
}
//到位置i分成了j段,i分到第j段
//dp[i][j]=dp[1][j-1]+dp[2][j-1]+...+dp[i-1][j-1],(a[k-1]+...+a[i])%j=0
signed main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
// freopen("in.txt","r",stdin);
#endif
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
pre[i]=pre[i-1]+a[i];
// pre[i]%=mod;
}
dp[0][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=i;++j)
{
for(int k=0;k<=i-1;++k)
{
if(query(k+1,i)%j==0)
{
dp[i][j]+=dp[k][j-1];
dp[i][j]%=mod;
}
}
}
}
int ans=0;
for(int j=1;j<=n;++j)
{
ans+=dp[n][j];
ans%=mod;
}
cout<<ans<<endl;
#ifdef LOCAL
fprintf(stderr,"%f\n",1.0*clock()/CLOCKS_PER_SEC);
#endif
return 0;
}
考虑优化,观察到
只跟前一个状态有关,可以先枚举 。- 记前缀和为
,转移条件 等价于 。 - 每次
第一位考虑的都是当前 的前缀。
于是开一个桶记录贡献,每次有
直接转移
时间复杂度
const int mod=1e9+7;
const int maxn=3005;
int n,a[maxn],dp[maxn][maxn],pre[maxn];
int query(int l,int r)
{
return pre[r]-pre[l-1];
}
//到位置i分成了j段,i分到第j段
//dp[i][j]=dp[1][j-1]+dp[2][j-1]+...+dp[i-1][j-1],(a[k]+...+a[i])%j=0
signed main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
// freopen("in.txt","r",stdin);
#endif
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
pre[i]=pre[i-1]+a[i];
// pre[i]%=mod;
}
dp[0][0]=1;
for(int j=1;j<=n;++j)
{
map<int,int> val;
for(int i=1;i<=n;++i)
{
val[pre[i-1]%j]+=dp[i-1][j-1];
val[pre[i-1]%j]%=mod;
dp[i][j]+=val[pre[i]%j];
}
}
int ans=0;
for(int j=1;j<=n;++j)
{
ans+=dp[n][j];
ans%=mod;
}
cout<<ans<<endl;
#ifdef LOCAL
fprintf(stderr,"%f\n",1.0*clock()/CLOCKS_PER_SEC);
#endif
return 0;
}
本文作者:vanueber
本文链接:https://www.cnblogs.com/vanueber/p/18668923
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步