P1341 无序字母对_欧拉路径_字符串

传送门

题目描述
给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现。

输入输出格式
输入格式:
第一行输入一个正整数n。

以下n行每行两个字母,表示这两个字母需要相邻。

输出格式:
输出满足要求的字符串。

如果没有满足要求的字符串,请输出“No Solution”。

如果有多种方案,请输出前面的字母的ASCII编码尽可能小的(字典序最小)的方案

输入输出样例
输入样例#1:
4
aZ
tZ
Xt
aX
输出样例#1:
XaZtX

P1341 无序字母对

此题看描述可知是一道欧拉回路相关题. (后面发现其实是欧拉路径)

通过所给的信息,找出一条欧拉回路,通过每一个顶点(字符)回到原点. 取字典序最小的输出.

只不过是把结点由数字表示变成了字母,用字母存即可,似乎不难. 有下面三个需要思考的问题

1.如何判断是否存在欧拉回路?
2.怎么找欧拉回路?
3.如何保证字典序最小?

第一个问题: OK, 吸取之前的教训,这回不能忘掉图不一定联通.那么,如果图不连通,肯定不存在欧拉回路.而如果图联通,当且仅当所有节点的度数都为偶数时,欧拉回路才成立
第二个问题:确认存在欧拉回路后, 找欧拉回路就是一个图的遍历过程.遍历这张图的每个点得到的路径,就一定是欧拉回路
第三个问题: 这就是遍历的技巧了,从小到大遍历,找到第一条路径就跳出,就是此题的答案了.

思考好了,下面准备Code

区分大小写, 只有字母, 那么我们只需存52个点. 数据较小.
大写为0-25, 小写为26-51;

我用邻接矩阵来存图.

咦, 突然发现这样存图是不对的. 序号不是连续的. 那么可以吗?

似乎也是可以, 只要记录哪些点存在,不存在的点直接忽略即可.

发现存图是可以优化的, 开个84大小的数组, 获取字符直接减’A’, 使得代码编辑较为简单 似乎很水没啥用

出现了顺序变反的问题. 开始不知道哪里错, 明明遍历的顺序是从小到大的,怎么会出错.

仔细检查之后,发现是push和递归的顺序反了.应该先push在递归dfs,不然就是里层的先push.它是一个堆栈的结构

然后提交了, 提交了, 可是只得了10分.呜呜呜.分数这么低看来就是样例过了,肯定是思路全错了.呜呜呜.

还是先测试测试数据再说

6
aZ
aT
ZT
ET
bE
Tb

测了这样一组数据, 发现我的答案错了,别人AC的代码是我心目中的对的, 那么我的代码有问题.

而且发现,欧拉回路好像就是把所有的边都经过一次,而点是可以经过多次的,好吧,点的问题是哈密顿回路.

Ok, 现在新增了一个Euler函数,用来找欧拉回路的.原来的dfs只是用来判断联通性.其实也可以合起来的.

… 还是10分. 哪里出了问题. 崩溃了.

额, 看了别人的题解之后猛然发现, 其实是求欧拉路径而不是回路, 只要有路径就OK.

呵呵, 现在是20分了,有了一倍的进步!

先贴20分代码, 然后从头再来.

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 84;
int G[maxn][maxn] = {}, degree[maxn] = {};
bool vis[maxn] = {}, exist[maxn] = {};
vector<int> node;

void dfs(int u) // 判断图是否联通 
{
    for (int i = 0; i < maxn; ++i) {
        if (G[u][i] && !vis[i] && exist[i]) {
            vis[i] = true;
            dfs(i);
        }
    }
}

void Euler(int u)
{
    for (int i = 0; i < maxn; ++i) {
        if (G[u][i]) {
            G[u][i] = G[i][u] = 0;
            node.push_back(i);
            Euler(i);
        }
    }
}

int main()
{
    int n, cnt = 0;
    char ca, cb;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int a, b;
        cin >> ca >> cb;
        a = ca - 'A';
        b = cb - 'A';
        G[a][b] = G[b][a] = 1;
        degree[a]++, degree[b]++; 
        exist[a] = exist[b] = true;
    }
    int _min = maxn;
    for (int i = 0; i < n; ++i) {
        if (degree[i] & 1) {  // 存在奇度点. 
            cnt++; 
            _min = min(i, _min);
        }
    }
    if (cnt > 2 || cnt == 1) {
        cout << "No Solution";
        return 0;
    }
    int param = -1;
    if (cnt == 2) param = _min;
    else {
        for (int i = 0; i < maxn; ++i) {
            if (exist[i]) {
                param = i; // 找到图中字典序最小的那个. 
                break;
            }
        }
    }
    vis[param] = true;
    dfs(param);
    for (int i = 0; i < maxn; ++i) {
        if (exist[i] && !vis[i]) { // 图不连通 
            cout << "No Solution";
            return 0;
        }
    }
    node.push_back(param);
    Euler(param);
    for (int i = 0; i < node.size(); ++i) {
        cout << char(node[i] + 'A');
    }
    cout << endl;
}
/*
4
aZ
tZ
Xt
aX

6
aZ
aT
ZT
ET
bE
Tb

2
ab
bc
*/

嗯, 按照一份题解的思路(几乎跟我的一毛一样啊, 而且它还没有判断联通性!) , 我重写了一份代码, 结果得了50分, 对了一半.

题解的思路似乎和我是一样的, 但是我仔细对比了一下差异, 发现它的存储欧拉路径的方式(时间, 位置)和我略有不同. 我是每个结点访问时存进去, 而他是在每个Euler函数结束部分push进去. 事实证明他是对的我是错了.

这是为什么呢? 很抓狂, 不知道为什么是对的, 它就是对的; 不知道哪里错了, 它确实错了.

总之记住了一点, 找欧拉路径上的结点, 要在函数最后压进去, 然后倒叙输出

#include <iostream>
#include <vector>
using namespace std;

const int maxn = 84;
int G[maxn][maxn] = {};
int degree[maxn] = {};
string error = "No Solution"; 
vector<int> node;

void Euler(int u)
{
    for (int i = 0; i < maxn; ++i) {
        if (G[u][i]) {
            G[u][i] = G[i][u] = 0;
            Euler(i);
        }
    }
    node.push_back(u); // 欧拉路径上的结点. 
}

int main()
{
    int n, cnt = 0;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        char a, b;
        cin >> a >> b;
        int c, d;
        c = a - 'A', d = b - 'A';
        G[c][d] = G[d][c] = 1;
        degree[c]++, degree[d]++;
    }
    int _min = maxn;
    for (int i = 0; i < maxn; ++i) {
        if (degree[i] & 1) {
            cnt++;
            _min = min(_min, i);
        }
    }
    if (cnt != 2 && cnt != 0) {
        cout << error;
        return 0;
    } 
    if (cnt == 0) {
        for (int i = 0; i < maxn; ++i) {
            if (degree[i]) {
                _min = i;
                break;
            }
        }
    }
    Euler(_min);
    for (int i = node.size() - 1; i >= 0; --i) {
        cout << char(node[i] + 'A');
    }
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/81229806