题解 P9229【扩展九连环】
看到这种跟汉诺塔非常像的解谜游戏,首先就是需要拆解它们的步骤。大体的步骤分三步:
- 把前面
n 个字符变成规则串。 - 消耗一步装上第
n+1 个环。 - 把前面
n 个字符变成11111...的形式。
我们设
- 对于
f(n,0,n)
我们从后往前扫描,如果当前的字符和最终这个位置需要的字符一致则跳过。此时我们找到了第一个不符的位置
写成式子就是
- 对于
f(n,n,n+1)
我们还是从后往前扫描到第一个不符合的位置,然后再对它进行变换。类似的,可以得到
我们注意到这个三维的状态中每一步都有一组数字是相同的。自然地,设
使用记搜实现,状态有
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rof(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int Maxn=2e3,Mod=1e9+7;
int n,ans,s[Maxn+5];
int f[Maxn+5][Maxn+5],g[Maxn+5][Maxn+5];
bool fv[Maxn+5][Maxn+5],gv[Maxn+5][Maxn+5];
int F(int x,int y);
int G(int x,int y);
inline int Get(int x,int y)
{
if(x==0) return 0; if(x==n+1) return 1;
return s[n-(x-y)];
}
inline int F(int x,int y)
{
if(x==0) return 0;
if(fv[x][y]) return f[x][y];
int res=0;
for(int i=x;i;i--) if(Get(x,i)!=Get(y,i))
{res=(F(i-1,y)+1+G(i-1,x))%Mod; break;}
fv[x][y]=1,f[x][y]=res; return res;
}
inline int G(int x,int y)
{
if(x==0) return 0;
if(gv[x][y]) return g[x][y];
int res=0;
for(int i=x;i;i--) if(Get(x,i)!=Get(y,i))
{res=(F(i-1,x)+1+G(i-1,y))%Mod; break;}
gv[x][y]=1,g[x][y]=res; return res;
}
int main()
{
scanf("%d",&n);
For(i,1,n) scanf("%1d",&s[i]);
ans=(F(n,0)+1+G(n,n+1))%Mod;
cout<<ans<<endl;
return 0;
}
注意到一个时间复杂度瓶颈在于扫描上。事实上,扫描需要解决的问题就是一个串不同前缀的最长公共后缀。我们再设一个动态规划数组
注意第二,三种转移应放到第一种转移后进行,否则会出现冲突。总时间复杂度
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rof(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int Maxn=2e3,Mod=1e9+7;
int n,ans,s[Maxn+5],h[Maxn+5][Maxn+5];
int f[Maxn+5][Maxn+5],g[Maxn+5][Maxn+5];
bool fv[Maxn+5][Maxn+5],gv[Maxn+5][Maxn+5];
int F(int x,int y);
int G(int x,int y);
inline int F(int x,int y)
{
if(x==0) return 0;
if(fv[x][y]) return f[x][y];
int res=0,id=(y>=1 && y<=n?n-(y-x):y);
int len=min(x,h[n][id]),i=x-len;
if(i) res=(F(i-1,y)+1+G(i-1,x))%Mod;
fv[x][y]=1,f[x][y]=res; return res;
}
inline int G(int x,int y)
{
if(x==0) return 0;
if(gv[x][y]) return g[x][y];
int res=0,id=(y>=1 && y<=n?n-(y-x):y);
int len=min(x,h[n][id]),i=x-len;
if(i) res=(F(i-1,x)+1+G(i-1,y))%Mod;
gv[x][y]=1,g[x][y]=res; return res;
}
int main()
{
scanf("%d",&n);
For(i,1,n) scanf("%1d",&s[i]);
For(i,1,n) For(j,1,n) if(s[i]==s[j])
h[i][j]=h[i-1][j-1]+1;
For(i,1,n) if(s[i]==0) h[0][i]=h[i][0]=h[0][i-1]+1;
For(i,1,n) if(s[i]==1) h[n+1][i]=h[i][n+1]=h[n+1][i-1]+1;
ans=(F(n,0)+1+G(n,n+1))%Mod;
cout<<ans<<endl;
return 0;
}