难度:提高+/省选-
题目类型:图论
提交次数:6
涉及知识:dfs/欧拉路径
题目描述
给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。
输入输出格式
输入格式:第一行输入一个正整数n。
以下n行每行两个字母,表示这两个字母需要相邻。
输出格式:输出满足要求的字符串。
如果没有满足要求的字符串,请输出“No Solution”。
如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案
输入输出样例
输入样例#1:
4 aZ tZ Xt aX
输出样例#1:
XaZtX
说明
【数据规模与约定】
不同的无序字母对个数有限,n的规模可以通过计算得到。
代码:
1 #include<iostream> 2 using namespace std; 3 int n; 4 const int N = 52; 5 int map[60][60]; 6 int du[60]; 7 char ans[2500]; 8 bool flag; 9 int change(char a){ 10 int askii = (int)(a); 11 if(askii<97){ 12 return askii-65; 13 } 14 else return askii-71; 15 } 16 char rechange(int x){ 17 if(x<=25) return char(x+65); 18 else return char(x+71); 19 } 20 void dfs(int x, int step){ 21 ans[step] = rechange(x); 22 if(step==n+1){ 23 flag = true; 24 return; 25 } 26 for(int i = 0; i < 52; i++){ 27 if(map[x][i]>0){ 28 map[x][i]--; 29 map[i][x]--; 30 dfs(i,step+1); 31 if(flag) break; 32 map[x][i]++; 33 map[i][x]++; 34 } 35 } 36 if(!flag) ans[step] = 0; 37 } 38 int main(){ 39 cin>>n; 40 int i; 41 for(i = 1; i <= n; i++){ 42 char a, b; 43 cin>>a>>b; 44 int x = change(a); 45 int y = change(b); 46 map[x][y] = 1; 47 map[y][x] = 1; 48 du[x]++; 49 du[y]++; 50 } 51 int flag = 100; 52 int minn = 100; 53 int odd = 0; 54 for(i = 0; i <= 51; i++){ 55 if(du[i]!=0&&minn==100) minn = i; 56 if(du[i]%2 == 1){ 57 flag = min(flag, i); 58 odd++; 59 } 60 61 } 62 if(odd!=0&&odd!=2) { 63 cout<<"No Solution"<<endl; 64 return 0; 65 } 66 if(flag!=100) dfs(flag, 1); 67 else dfs(minn,1); 68 for(i = 1; i <= n+1; i++){ 69 cout<<ans[i]; 70 } 71 cout<<endl; 72 return 0; 73 }
备注:
欧拉路径就是一笔画,在图中仅有0或2个奇数入度点时存在。昨天下午看了半天求欧拉路径的算法,今天老师嘲讽一通说我看的是假的算法。。dfs+删边就可以了。虽然我很不愿意接受,但这么写写竟然是对的,而且效率还可以。
为什么是对的很简单。从一个奇数入度点出发,如果全是偶数入度点,就从最小的点开始,dfs,走到无路可走时就找到了一个解。注意一定要回溯(否则走入一条死胡同就结束了),肯定不能一到某个点就输出这个点(此时不一定走的就是正确的路),这也是dfs加一个step参量的必要性(最后一次更新一定是正解)(回溯到这个点说明上一次从这个点出去选择的方向走不完图,所以换个方向走)。
字典序很好办。从小到大搜就可以了。
后来codevs过了,洛谷最后一个点过不了。看了一眼讨论,发现ans数组开小了。n的规模可以算,应该是52*52,开成2500就过了。