P3205 [HNOI2010]合唱队
P3205 [HNOI2010]合唱队
区间DP——取一端
思:
根据题意我们发现,每次排队的时候,会出现两种情况
- 当前排入的人(即初始队列最后一人)比初始队列中前一个人矮,排到最左边
- 当前排入的人(同上)比初始队列中前一个人高,排到最右边
可从初始队列最后一人切入。
设置状态:
0表示从插入队头,1表示插入队尾。
原始推目标,目标再推回原始。感觉有点绕,画图大法非常好。
(以最后一人插入队头为例,插入队尾举一反三.
通过
举样例:
4
1701 1702 1703 1704
枚举区间1~3(1701,1702,1703)时
1701的前面若是1702(a[l]<a[l+1]),则原始序列中1702是比前一个人矮才来到2~3的队首。
1701的前面若是1703(a[l]<a[r]),则原始序列1703是比前一个人高才来到2~3的队尾。
(防止混淆,不是1702既来队首,又有来队尾的可能。
注意初始化。
转移方程为:
最后一人在队首时
if(h[l+1]>h[l]) f[l][r][0]+=f[l+1][r][0];//最后一人的前面的人来l+1~r的队头
if(h[r]>h[l]) f[l][r][0]+=f[l+1][r][1];//来l+1~r的队尾
在队尾
if(h[l]<h[r]) f[l][r][1]+=f[l][r-1][0];//来l~r-1的队头
if(h[r-1]<h[r]) f[l][r][1]+=f[l][r-1][1];//来l~r-1的队尾
枚举区间段即可。
#include <bits/stdc++.h>
using namespace std;
const int N =1e3+10,mod=19650827;
int f[N][N][2],h[N],n;
void add(int &x,int k){//计算取模
x=(x+k)%mod;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
f[i][i][0]=1;//初始化
}
for(int len=2;len<=n;len++){//枚举区间长度,从2开始,1没必要。
for(int l=1;l+len-1<=n;l++){//枚举区间段
int r=l+len-1;
if(h[l+1]>h[l]) add(f[l][r][0],f[l+1][r][0]);
if(h[r]>h[l]) add(f[l][r][0],f[l+1][r][1]);
if(h[l]<h[r]) add(f[l][r][1],f[l][r-1][0]);
if(h[r-1]<h[r]) add(f[l][r][1],f[l][r-1][1]);
}
}
cout<<(f[1][n][0]+f[1][n][1])%mod;//总方案为最后一人来队首和队尾方案之和,取模。
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】