P3685 [CERC2016] Invisible Integers 题解
对于方向相同的两个提示
如果我们按这个方向依次填数,若已经填到了
若
(以下所有下标均从
考虑如果只能往右走怎么做。我们从左到右依次填数。考虑 DP,设
- 停止考虑
i ,转而考虑j 。转移为f(S\cup\{j\},j,0)\leftarrow f(S,i,l) ,需满足j 可以接在i 的第l 位之后。 - 填一个数。我们可以直接填
a_{i,l} ,转移为f(S,i,l+1)\leftarrow f(S,i,l)+1 。
考虑扩展到一般情况。设
注意:我们仍然是从左到右依次填数,所以向左走的提示要从后往前匹配,向右走的提示要从前往后匹配,这导致了两个方向的提示转移方式的不同。
有三种转移:
- 把
i 换成k 。转移为f(S\cup\{k\},k,x,j,r)\leftarrow f(S,i,0,j,r) ,其中x 表示所有满足i 能接在k 的第x 位之后的x 。 - 把
j 换成k 。转移为f(S\cup\{k\},i,l,k,0)\leftarrow f(S,i,l,j,r) ,需满足k 能接在j 的第r 位之后。 - 填一个数。枚举填的数
k ,转移为f(S,i,l-[a_{i,l-1}=k],j,r+[a_{j,r}=k])\leftarrow f(S,i,l,j,r)+1 。需满足p_{i,k}\le l (注意不是l-1 )且p_{j,k}\le r 。
有亿点细节:
我们需要想办法记录当前考虑的提示是否为对应方向的第一个提示。具体地,我们可以新增一个编号为
DP 初值为
- 停止新增向左的提示。转移为
f(S,n,0,j,r)\leftarrow f(S,i,0,j,r) 。
注意转移顺序。时间复杂度
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
int n,ht[15][15],pos[15][15],st[15],len[15],tr[15][15];
int f[1029][11][11][11][11],cur,ans;
inline int get_tr(int x,int y){
if((st[x]&st[y])^st[y])return inf;
int a=pos[y][ht[x][len[x]-1]],b,i;
if(a==inf)return len[x];
for(i=len[x]-1;i;--i){
b=a,a=pos[y][ht[x][i-1]];
if(a==inf || a>b)break;
}
return i;
}
inline void upd(int &x,int y){x>y?x=y:0;}
int main(){
scanf("%d",&n);
memset(pos,0x3f,sizeof(pos));
memset(tr,0x3f,sizeof(tr));
memset(f,0x3f,sizeof(f));
for(int i=0,j,x;i<n;++i){
j=0;
while(scanf("%d",&x) && x){
ht[i][j]=--x;
pos[i][x]=j++;
st[i]|=1<<x;
}
len[i]=j;
}
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
if(i!=j)tr[i][j]=get_tr(i,j);
f[0][n][0][n][0]=0;
for(int i=0;i<n;++i)
f[1<<i][i][len[i]][n][0]=0;
ans=inf;
for(int S=0;S<(1<<n);++S){
for(int i=0;i<=n;++i){
for(int l=len[i];~l;--l){
for(int j=0;j<=n;++j){
for(int r=0;r<=len[j];++r){
if((cur=f[S][i][l][j][r])>=inf)continue;
if(S==(1<<n)-1 && l==0 && r==len[j])upd(ans,cur);
for(int k=0;k<n;++k){
if(S>>k&1)continue;
if(i!=n && l==0)
for(int x=tr[k][i];x<=len[k];++x)
upd(f[S|(1<<k)][k][x][j][r],cur);
if(j==n || r>=tr[j][k])
upd(f[S|(1<<k)][i][l][k][0],cur);
}
if(i!=n && l==0)upd(f[S][n][0][j][r],cur);
for(int k=0,L,R;k<9;++k){
if(i==n)L=0;
else{
if(pos[i][k]>l)continue;
L=l-(pos[i][k]==l-1);
}
if(j==n)R=0;
else{
if(pos[j][k]>r)continue;
R=r+(pos[j][k]==r);
}
upd(f[S][i][L][j][R],cur+1);
}
}
}
}
}
}
printf("%d\n",ans<inf?ans:-1);
return 0;
}