CXXI.[GYM100134I][NEERC2012]Identification of Protein
debug5h,精神崩溃。
首先,很容易想到把所有东西都乘上 \(10^5\) 变成整数。然后,因为 \(\gcd(9705276,12805858)=2\),所以在字符串长度 \(\leq400\) 时,每个值被表示成二者倍数之和的方式是唯一的。于是我们可以把所有合法的值唯一转换成“有 \(p_i\) 个 P
和 \(q_i\) 个 Q
”的表达形式。因此,整条字符串中所具有的 P
数和 Q
数也就确定了。分别设其为 \(n\) 和 \(m\)。
然后,一条前缀就唯一对应了一条后缀,反之亦然。于是我们便可以建出一张矩阵,\(a_{i,j}\) 表示有多少个串中有 \(i\) 个 P
和 \(j\) 个 Q
(当然,作为前缀和后缀)。此时,一条字符串就对应了一条 \((0,0)\rightarrow(n,m)\) 的路径,而所有可以被表示成其前缀或后缀的值的数量就是路径经过所有位置的权值和。
但是,一个值如果同时作为前缀和后缀出现,是不能被计算两遍的。所以我们设计DP状态时,要同时记录下正着的和反着的位置,以在上述情况时及时判断出来。我们设 \(f_{i,j,k}\) 表示当前填到前后缀各第 \(i\) 个字符,且前缀里已经填了 \(j\) 个 P
,后缀里已经填了 \(k\) 个 Q
的最优收益。采取记忆化搜索进行DP,复杂度 \(O(n^3)\)。
要注意一堆细节,例如实际上当总串长度为奇或偶时,前后缀相遇处时的处理是不一样的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P=9705276;
const int Q=12805858;
const int inf=-0x3f3f3f3f;
int q,n,m,s,g[410][410],f[210][210][210],opt[210][210][210];
ll a[100100],mx;
double db;
pair<int,int>pr;
pair<int,int>pt(ll ip){
for(int i=0;ip>=0;ip-=P,i++)if(!(ip%Q))return make_pair(i,ip/Q);
return make_pair(-1,-1);
}
int dfs(int stp,int x1,int x2){
int &now=f[stp][x1][x2];
if(now!=-1)return now;
if((x1+x2-(s&1)>n||stp-x1+stp-x2-(s&1)>m)||!(x1<=n&&x2<=n&&stp-x1<=m&&stp-x2<=m))return now=inf;
now=g[x1][stp-x1];
if(x1!=x2&&(x1!=n-x2||stp-x1!=m-(stp-x2)))now+=g[x2][stp-x2];
if(stp==((s+1)>>1))return now;
if(x1+x2>n||stp-x1+stp-x2>m)return now=inf;
int A=dfs(stp+1,x1,x2);
int B=((s&1)&&(stp==(s>>1)))?inf:dfs(stp+1,x1+1,x2);
int C=((s&1)&&(stp==(s>>1)))?inf:dfs(stp+1,x1,x2+1);
int D=dfs(stp+1,x1+1,x2+1);
int M=max({A,B,C,D});
now+=M;
if(A==M)opt[stp][x1][x2]=1;
else if(B==M)opt[stp][x1][x2]=2;
else if(C==M)opt[stp][x1][x2]=3;
else if(D==M)opt[stp][x1][x2]=4;
return now;
}
char res[410];
int main(){
freopen("identification.in","r",stdin);
freopen("identification.out","w",stdout);
scanf("%d",&q),memset(f,-1,sizeof(f));
for(int i=1;i<=q;i++){
scanf("%lf",&db);
a[i]=db*100000+0.1;
mx=max(mx,a[i]);
}
pr=pt(mx),n=pr.first,m=pr.second,s=n+m;
for(int i=1;i<=q;i++){
pr=pt(a[i]);
if(pr==make_pair(-1,-1))continue;
if(pr.first>n||pr.second>m)continue;
g[pr.first][pr.second]++;
if((pr.first<<1)!=n||(pr.second<<1)!=m)g[n-pr.first][m-pr.second]++;
}
dfs(0,0,0);
for(int stp=0,x1=0,x2=0;stp+1<=s-stp;stp++){
if(opt[stp][x1][x2]==1)res[stp+1]='Q',res[s-stp]='Q';
else if(opt[stp][x1][x2]==2)res[stp+1]='P',res[s-stp]='Q',x1++;
else if(opt[stp][x1][x2]==3)res[stp+1]='Q',res[s-stp]='P',x2++;
else if(opt[stp][x1][x2]==4)res[stp+1]='P',res[s-stp]='P',x1++,x2++;
}
printf("%s\n",res+1);
return 0;
}