P3624 DNA - dp求字典序
P3624 DNA - dp
题意
这段ai写的洛谷没过
- DNA 序列由
A、C、G、T四种字符组成,其中N表示是 A/C/G/T 中的任意一个。 - 四种核苷酸的优先级顺序:
A>C>G>T。 - 范式-1:序列中每个核苷酸都 ≥ 其右边的核苷酸(按优先级比较)。
- 范式-j(j>1):要么是范式-(j-1),要么是范式-(j-1) 和范式-1 的连接。
任务:给定一个包含 N 的未完成序列,找出所有适合该序列的范式-K DNA 序列中,优先级排序的第 R 个。
输入:
- 第一行:
MKR(序列长度,范式等级K,要找的排名R) - 第二行:长度为
M的字符串(包含 A/C/G/T/N)
输出:
- 第
R个适合该序列的范式 -KDNA序列
思路
显然我们只要知道在每个 \(N\) 的位置知道其所有选择的对应的方案数,通过不断确定状态,来限定其后面的最低排名,就可以得到答案。
不过想要知道其后面的方案数,显然要从后面向前计算。如果作的是 \(N\) 前面的方案数,并最后从后向前推的时候,会发现限制出来的 DNA 序列不是按优先级升序的,所以只能从后向前计算。
考虑 \(dp\),容易想到开一维存位置,然后由于范式 \(-1\) 对顺序有要求,并且 \(N\) 有 \(4\) 种选择,所以还要开一维表示当前的字母,我们要求的是范式 \(-K\) 的方案数,而 \(-K\) 的方案数显然可以由 \(-(K-1)\) 推得,所以再开一维表示当前的范式等级。即:
\(dp_{i,j,b}\) 表示第 \(i\) 个位置,当前范式等级为 \(j\),当前字母为 \(b\) 的方案数。
思考如何转移:
- 如果当前是 \(N\),那么字母需要枚举,否则 \(b\) 是确定的。
- 范式等级需要枚举到 \(K\),发现如果当前优先级和后面连上了,那当前等级 \(j\) 可由 \(i+1\) 的 \(j\) 推得,否则由 \(i+1\) 的 \(j-1\) 推得,即当前 \(j\) 由 \(j-(b>bb)\) 推得。
- 所有比 \(K\) 小的等级也属于范式 \(-K\) 的方案数,这里作前缀和优化。
最后得到:
\[dp_{i,j,b} = \sum_{bb=1}^4 {dp_{i+1,j-(b>bb),bb}}
\]
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn = 1e5+10;
int n,k,r;
int wi[maxn],dp[maxn][15][5];
int c2i(char c)
{
if(c=='A') return 1;
if(c=='C') return 2;
if(c=='G') return 3;
if(c=='T') return 4;
return 0;
}
char i2c(int x)
{
if(x==1) return 'A';
if(x==2) return 'C';
if(x==3) return 'G';
return 'T';
}
signed main()
{
scanf("%lld%lld%lld",&n,&k,&r);
for(int i=1;i<=n;++i)
{
char c;
scanf(" %c",&c);
wi[i]=c2i(c);
}
if(!wi[n])
{
for(int i=1;i<=4;++i)
{
dp[n][1][i]=1;
}
}
else
{
dp[n][1][wi[n]]=1;
}
for(int i=n-1;i>=1;--i)
{
if(wi[i])
{
for(int j=1;j<=k;++j)
{
for(int bb=1;bb<=4;++bb)
{
dp[i][j][wi[i]]+=dp[i+1][j-(wi[i]>bb)][bb];
}
}
}
else
{
for(int b=1;b<=4;++b)
{
for(int j=1;j<=k;++j)
{
for(int bb=1;bb<=4;++bb)
{
dp[i][j][b]+=dp[i+1][j-(b>bb)][bb];
}
}
}
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=k;++j)
{
for(int b=1;b<=4;++b)
{
dp[i][j][b]+=dp[i][j-1][b];
}
}
}
int lst=0;
for(int i=1;i<=n;++i)
{
if(wi[i])
{
printf("%c",i2c(wi[i]));
k-=(lst>wi[i]);
lst=wi[i];
}
else
{
int b=1;
for(;b<=4 && r>dp[i][k-(lst>b)][b];++b)
{
r-=dp[i][k-(lst>b)][b];
}
k-=(lst>b);
lst=b;
printf("%c",i2c(b));
}
}
return 0;
}

浙公网安备 33010602011771号