题解 P4683 【[IOI2008] Type Printer 打印机】

StudyingFather

2019-11-28 20:24:28

Solution

首先建出 Trie 树,容易发现我们的目标是确定一种 DFS 序,使得在遍历所有节点的前提下走过的边数尽可能少。 如果走完最后一个节点要返回根节点的话,那么显然每条边至少要走两次(递归的时候走一次,回溯的时候还要走一次)。 在本题中,我们走完最后一个节点后就不需要返回了。这意味着我们可以少走从最后一个点到根的这些边。 怎样让少走的边尽可能多呢?显然只需让最深的节点最后走到即可。 于是我们可以按这样的方式进行 DFS:先找出每个节点深度最深的子树,每次 DFS 先走完其他子树,再遍历最深的子树即可。 ```cpp #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int sig=26; char res[1000005]; int ans; struct trie { struct node { int son[sig],end,ls,dep,maxd; }tr[500005]; int cnt; void insert(char* s) { int u=0; for(int i=0;s[i];i++) { int &v=tr[u].son[s[i]-'a']; if(!v)v=++cnt; u=v; } tr[u].end=1; } void dfs1(int u,int fa) { tr[u].maxd=tr[u].dep=tr[fa].dep+1; tr[u].ls=-1; for(int i=0;i<sig;i++) { int v=tr[u].son[i]; if(v) { dfs1(v,u); if(tr[v].maxd>tr[u].maxd) { tr[u].maxd=tr[v].maxd; tr[u].ls=i; } } } } void dfs2(int u) { if(tr[u].end)res[++ans]='P'; int ls=tr[u].ls; for(int i=0;i<sig;i++) { int v=tr[u].son[i]; if(v&&i!=ls) { res[++ans]='a'+i; dfs2(v); } } if(ls!=-1) { res[++ans]='a'+ls; dfs2(tr[u].son[ls]); } res[++ans]='-'; } }t; char s[25]; int main() { int n; scanf("%d",&n); int maxl=0; for(int i=1;i<=n;i++) { scanf("%s",s); maxl=max(maxl,(int)strlen(s)); t.insert(s); } t.dfs1(0,0); t.dfs2(0); ans-=maxl+1;//排除走完最后一个点的回溯过程 printf("%d\n",ans); for(int i=1;i<=ans;i++) printf("%c\n",res[i]); return 0; } ```