题解 CF510C【Fox and Names】

Enterpr1se

2021-02-25 10:11:55

Solution

>~~没有图,就要创造图。~~ ~~——鲁迅~~ 看到这题我首先想到的算法是Floyd【我们老师当时教的是如果需要比较多个数(1000 个以下),大概率是使用 Floyd】,但是算法标签并没有Floyd(尽管如此一会你们就会发现 Floyd 真的很有用),反倒是有一个“拓扑排序”。但是无论哪种算法都需要我们先建图,这下整道题的逻辑就基本清楚了。 ~~(秦风式战术停顿)~~ 但事实真的如此吗? 其实字典序中有很多不容易被注意到的小细节,所以我们在开始做题之前,不妨先看看[字典序的定义](https://baike.baidu.com/item/%E5%AD%97%E5%85%B8%E5%BA%8F/7786229)。其中比较方法如下(至于我为啥要写这么基础的东西,看到后面就知道了): >对于两个长度分别为 $n$ 和 $m$ 的字符串 $a$ 和 $b$, >+ 首先比较 $a_1$ 和 $b_1$,若 $a_1$ 在字母表中比 $b_1$ 更靠前,则 $a$ 比 $b$ 更靠前。 >+ 若 $a_1 = b_1 $,则比较字符串 $\{a_1,a_2,a_3,...,a_n\}$ 和字符串 $\{b_1,b_2,b_3,...,b_m\}$。 >特判: >+ 若 $a=\{b_1,b_2,b_3,...,b_n\}$,则 $a$ 比 $b$ 更靠前。 反过来,我们就可以得到通过字典序求字母表的过程: >对于两个长度分别为 $n$ 和 $m$ 的字符串 $a$ 和 $b$, >+ 首先比较 $a_1$ 和 $b_1$,若 $a$ 比 $b$ 更靠前,则 $a_1$ 在字母表中比 $b_1$ 更靠前。 >+ 若 $a_1 = b_1 $,则比较字符串 $\{a_1,a_2,a_3,...,a_n\}$ 和字符串 $\{b_1,b_2,b_3,...,b_m\}$。 由此我们就可以得到第一段代码:求字母间关系: ```cpp void getrel(string a,string b){ if(!a.length() || !b.length()) return; if(a[0]==b[0]){ if(a.length()>1 && b.length()>1) getrel(a.substr(1),b.substr(1)); return; } add(a[0],b[0]); return; } ``` 接着我们再来看~~骗分助手~~ `Impossible` 的成因,无非三种: + Case 1:得出结论中字母 $letter_a$ 比 $letter_b$ 靠前,且 $letter_b$ 比 $letter_a$ 靠前。 + Case 2:得出结论中 $letter_a$ 比其本身更靠前。 + Case 3:字符串 `a.substr(0,x)` ($x<length(a)$)比字符串 `a` 更靠前。(这一点困扰了我这个蒟蒻将近半个小时) 接下来整段代码的逻辑就出来了: 1. 输入,判断是否存在 Case 3 2. 链式前向星上建图(上面已经贴了代码) 3. 将链式前向星上的图转换到一个矩阵上(方便运行 Floyd) 并统计出度(方便运行拓扑排序) 4. 通过Floyd找出所有可以推出的字母关系 5. 在矩阵中判断是否有 Case 1 和 Case 2 6. 运行拓扑排序,找到一组解 完整代码: ```cpp //Luogu-CF510C Solution Version 题解版本 //Luogu @W53729 (Userid 363523) //@_Qijia (Userid 363524) AK IOI! #include<iostream> #include<string> #include<queue> #define regll register long long #define regint register int #define regshort register short #define _Qijia using #define AK namespace #define IOI std _Qijia AK IOI;//大佬保佑我AC(? int n,last[300],rcnt,ind[300]; string name[105],ans; bool rel[300][300]; char curr,targ; queue<char> qu; struct edge{ char to; int prev; } fig[10005]; void add(int s,int e){ ++rcnt; fig[rcnt]={e,last[s]}; last[s]=rcnt; } void getrel(string a,string b){ if(!a.length() || !b.length()) return; if(a[0]==b[0]){ if(a.length()>1 && b.length()>1) getrel(a.substr(1),b.substr(1)); return; } add(a[0],b[0]); return; } void trans(){ for(char i='a';i<='z';++i) for(regint j=last[i];j;j=fig[j].prev){ rel[i][fig[j].to]=true; ++ind[fig[j].to]; } return; } void floyd(){ for(char k='a';k<='z';++k) for(char i='a';i<='z';++i) for(char j='a';j<='z';++j) if(rel[i][k] && rel[k][j]) rel[i][j]=true; return; } signed main(){ ios::sync_with_stdio(false);//加速黑科技 //Step 1 cin>>n; for(regint i=1;i<=n;++i) cin>>name[i]; for(regint i=1;i<=n-1;++i){ if(name[i].length()>name[i+1].length() && name[i+1]==name[i].substr(0,name[i+1].length())){ cout<<"Impossible"; return 0; } } //Step 2 for(regint i=1;i<=n;++i) for(regint j=i+1;j<=n;++j) getrel(name[i],name[j]); //Step 3 trans(); //Step 4 floyd(); //Step 5 for(char i='a';i<='z';++i) for(char j=i+1;j<='z';++j) if(rel[i][j] && rel[j][i]){ cout<<"Impossible"; return 0; } for(char i='a';i<='z';++i) if(rel[i][i]){ cout<<"Impossible"; return 0; } //Step 6 for(char i='a';i<='z';++i) if(!ind[i]) qu.push(i); while(qu.size()){ curr=qu.front(); qu.pop(); cout<<curr; for(regint j=last[curr];j;j=fig[j].prev){ targ=fig[j].to; --ind[targ]; if(!ind[targ]) qu.push(targ); } } return 0; } ```