1 条题解
-
0
自动搬运
来自洛谷,原作者为

chen_03
AFO | 垫底人,啥都不会 | 谁能赐予我一个脑子? | Brute force 出不了奇迹。搬运于
2025-08-24 21:51:35,当前版本为作者最后更新于2022-03-15 17:07:27,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
对于方向相同的两个提示 ,我们考虑如果 能接在 的第 位之后,应当满足什么条件:
- 作为集合时,;
- 从第 位开始往后的部分(包括第 位)是 的子序列。
如果我们按这个方向依次填数,若已经填到了 的第 位,那么我们可以选择停止考虑 ,转而考虑 。
若 满足条件,则 也满足条件。我们可以对于每对 预处理出这个最小的 ,或者发现不存在这样的 。
(以下所有下标均从 开始。记 表示提示 的第 位; 表示数字 在提示 中的出现位置,若没有出现则为 ; 表示提示 的长度)
考虑如果只能往右走怎么做。我们从左到右依次填数。考虑 DP,设 表示已经考虑了集合 中的提示,当前正在考虑提示 ,已经填了 的第 位(正在等待第 位)时的最短长度。有两种转移:
- 停止考虑 ,转而考虑 。转移为 ,需满足 可以接在 的第 位之后。
- 填一个数。我们可以直接填 ,转移为 。
考虑扩展到一般情况。设 表示已经考虑了集合 中的提示,当前正在考虑的向左走的提示为 ,已经填了 的第 位(正在等待第 位),当前正在考虑的向右走的提示为 ,已经填了 的第 位(正在等待第 位)时的最短长度。
注意:我们仍然是从左到右依次填数,所以向左走的提示要从后往前匹配,向右走的提示要从前往后匹配,这导致了两个方向的提示转移方式的不同。
有三种转移:
- 把 换成 。转移为 ,其中 表示所有满足 能接在 的第 位之后的 。
- 把 换成 。转移为 ,需满足 能接在 的第 位之后。
- 填一个数。枚举填的数 ,转移为 $f(S,i,l-[a_{i,l-1}=k],j,r+[a_{j,r}=k])\leftarrow f(S,i,l,j,r)+1$。需满足 (注意不是 )且 。
有亿点细节:
我们需要想办法记录当前考虑的提示是否为对应方向的第一个提示。具体地,我们可以新增一个编号为 的提示作为占位符(可以认为它的长度为 )。在 中,若 ,则表示不再新增向左的提示;若 ,则表示还没有考虑过向右的提示。
DP 初值为 。需要新增一种转移:
- 停止新增向左的提示。转移为 。
注意转移顺序。时间复杂度 。
#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; }
- 1
信息
- ID
- 1243
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者