P3205 [HNOI2010]合唱队
P3205 [HNOI2010]合唱队
题解
区间DP唉
不会写怎么办QAQ
先暴搜
暴力:
根据理想队形找原序列,找第一个人不好找,但是可以确定最后一个人是谁,然后就可以一次推测前一个人,知道搜出一个合法序列
显然,最后一个人只可能是理想序列的最左端或最右端
然后在剩下的区间里面,找到合法的下一个人,继续搜,知道搜到只剩一个人的区间,判断一下合不合法,就可以判断能否得到一个合法序列
为了有效避免一开始的分类讨论(也就是从最左端开始还是从最右端开始算最后一个)
我们默认最后一个人的标号为 0 ,因为此时第0个人的身高为0,在整个区间最左端,肯定比左右区间的人都矮
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<cstdlib> #include<queue> using namespace std; typedef long long ll; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int mod=19650827; int n; int a[1005]; ll ans=0; ll dfs (int now,int l,int r) //当前搜索的人的标号,下面要搜索的区间[l,r] { if(l==r) return (now>l&&a[now]>a[l])||(now<l&&a[now]<a[l]); //区间只剩一个人喽,也要最后判断一下 if(now>r&&a[now]<a[l]&&a[now]<a[r]) return 0; if(now<l&&a[now]>a[l]&&a[now]>a[r]) return 0; //不合法,直接计0 ll res=0; if(now>r&&a[now]>a[l]||now<l&&a[now]<a[l]) res=res+dfs(l,l+1,r)%mod; if(now>r&&a[now]>a[r]||now<l&&a[now]<a[r]) res=res+dfs(r,l,r-1)%mod; return res%mod; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); ans=0; ans=ans+dfs(0,1,n)%mod; printf("%lld\n",ans); return 0; }
记忆化:
不好意思我 WA 了几个点
考虑区间DP:
设置两个数组:
f[ i ][ j ] 区间 [ i , j ] 最后一个选择左端点的合法序列数
g[ i ][ j ] 区间 [ i , j ] 最后一个选择右端点的合法序列数
代码
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<cstdlib> #include<queue> using namespace std; typedef long long ll; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int mod=19650827,maxn=214748360,minn=-1; int n; int a[1005]; ll f[1005][1005],g[1005][1005]; int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); a[0]=3000; for(int i=1;i<=n;i++) f[i][i]=1; for(int l=2;l<=n;l++) for(int i=1;i+l-1<=n;i++) { int j=i+l-1; f[i][j]=(f[i+1][j]*(a[i]<a[i+1])%mod+g[i+1][j]*(a[i]<a[j])%mod)%mod; g[i][j]=(f[i][j-1]*(a[j]>a[i])%mod+g[i][j-1]*(a[j]>a[j-1])%mod)%mod; } printf("%lld\n",(f[1][n]+g[1][n])%mod); return 0; }