【洛谷P1127】词链

今天又来学习图论喵!

我们今天主要讲解一个东西:欧拉回路(路径)
什么是欧拉路径呢?
当然是指一笔画能把所有边都过一遍(包括重边)
而欧拉回路就是指起点和终点一样的回路
那么 我们怎么输出欧拉回路呢?
首先 我们要了解一个性质 在有向图里,如果存在欧拉路径 那么它存在有且仅有一个点出度比入度大一(起点),一个点入度比出度大一(终点),其他点入度和出度相等或者所有点入度等于出度(欧拉回路)
在无向图 就是直接度数为奇数的点就是起点
当然,存在欧拉回路的必要条件还有一个:图是连通的,不存在孤立的点
这个可以用并查集/DFS/以后学的tarjan缩点啥的判断
我们先看一下有向图欧拉路径的模版:
(通常会要求字典序最小,这个时候要排个序)最后用栈倒序输出
而且一般用邻接矩阵或者vector邻接表 链式前向星不好排序
洛谷P7771 欧拉路径

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
int x,y,sx=1;
int del[100005];
int snum,fnum;
vector<int>ver[100005];
stack<int>s;
int rd[100005],cd[100005];
void dfs(int x){
    for(int i=del[x];i<ver[x].size();i=del[x]){
        del[x]=i+1;
        dfs(ver[x][i]);
    }
    s.push(x);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        rd[y]++;cd[x]++;
        ver[x].push_back(y);
    }
    for(int i=1;i<=n;i++) sort(ver[i].begin(),ver[i].end());
    bool flag=true;
    for(int i=1;i<=n;i++){
        if(rd[i]==cd[i]) continue;
        else{
            flag=false;
            if(rd[i]==cd[i]+1){
                fnum++;
            }
            else if(cd[i]==rd[i]+1){
                snum++;sx=i;
            }
            else{
                cout<<"No";
                system("pause");
                return 0;
            }
        }
    }
    if(!flag&&!(snum==1&&fnum==1)){
        cout<<"No";
        system("pause");
        return 0;
    }  
    dfs(sx);
    while(!s.empty()){
        cout<<s.top()<<' ';
        s.pop();
    }
    system("pause");
    return 0;
}

然后再看一下无向图的:
洛谷P2731 骑马修栅栏

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int m,n;
int ma[505][505];
int d[505];
int x,y;
int st=505;
stack<int>s;
void dfs(int now){
    for(int i=1;i<=n;i++){
        if(ma[now][i]>=1){
            ma[now][i]--;
            ma[i][now]--;
            dfs(i);
        }
    }
    s.push(now);
}
int main(){
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        n=max(n,max(x,y));
        st=min(st,min(x,y));
        ma[x][y]++;
        ma[y][x]++;
        d[x]++;
        d[y]++;
    }
    for(int i=1;i<=n;i++){
        if(d[i]%2){
            st=i;
            break;
        }
    }
    dfs(st);
    while(!s.empty()){
        cout<<s.top()<<endl;
        s.pop();
    }
    system("pause");
    return 0;
}

那么 进入我们今天的重点:A的第一道蓝题 词链!
看看题目:

P1127 词链

题目描述

如果单词 \(X\) 的末字母与单词 \(Y\) 的首字母相同,则 \(X\)\(Y\) 可以相连成 \(X.Y\)。(注意:\(X\)\(Y\) 之间是英文的句号 .)。例如,单词 dog 与单词 gopher,则 doggopher 可以相连成 dog.gopher

另外还有一些例子:

  • dog.gopher
  • gopher.rat
  • rat.tiger
  • aloha.aloha
  • arachnid.dog

连接成的词可以与其他单词相连,组成更长的词链,例如:

aloha.arachnid.dog.gopher.rat.tiger

注意到,. 两边的字母一定是相同的。

现在给你一些单词,请你找到字典序最小的词链,使得每个单词在词链中出现且仅出现一次。注意,相同的单词若出现了 \(k\) 次就需要输出 \(k\) 次。

输入格式

第一行是一个正整数 \(n\)\(1 \le n \le 1000\)),代表单词数量。

接下来共有 \(n\) 行,每行是一个由 \(1\)\(20\) 个小写字母组成的单词。

输出格式

只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号 ***

输入输出样例 #1

输入 #1

6
aloha
arachnid
dog
gopher
rat
tiger

输出 #1

aloha.arachnid.dog.gopher.rat.tiger

说明/提示

  • 对于 \(40\%\) 的数据,有 \(n \leq 10\)
  • 对于 \(100\%\) 的数据,有 \(n \leq 1000\)

解法&&个人感想:

看到欧拉回路已经有思想了
首先并查集判断连通性 找起点 然后终点输出
这题是n个 所以搜索带个数量 到n就输出
好了 就是我们的思想了:
把首尾字母视为边!而不是单词!然后用结构体存边(单词)
首先 如果是单词你不好处理像单词只有一个字母的情况
而且 如果是3 w w w这样的极端样例也不好处理
而这点我也是参考其他大佬的解法才看出来的
唉 还是比较菜
下面 我们开始吧!

#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s[1005];
int n;
int fa[27];
int rd[27];
int cd[27];
int sx,fx;
int sum;
struct node{
    int to;
    int num;
    string a;
};
vector<node>ver[1005];
int cnt=0;
int st[1005];
int exist[27];
int del[27];
string res[1005];
int total_u;
int vis[1005];
int get(int x){
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
void merge(int x,int y){
    fa[get(x)]=get(y);
    return ;
}
void dfs(int now,int num,int count){
    if(count==n){
        for(int i=1;i<=sum;i++){
            if(i!=1) cout<<'.';
            cout<<res[i];
        }
        system("pause");
        exit(0);
    }
    for(int i=0;i<ver[now].size();i++){
        if(vis[ver[now][i].num]) continue;
        else{
            res[++sum]=ver[now][i].a;
            vis[ver[now][i].num]=1;
            dfs(ver[now][i].to,ver[now][i].num,count+1);
            sum--;
            vis[ver[now][i].num]=0;
        }//记得回溯
    }
    return ;
}
int main(){
    scanf("%d\n",&n);
    for(int i=1;i<=n;i++){
        cin>>s[i];
    }
    sort(s+1,s+1+n);
    for(int i=1;i<=n;i++){
        int s_begin=s[i][0]-'a'+1;
        int s_end=s[i][s[i].length()-1]-'a'+1;
        rd[s_end]++;cd[s_begin]++;
        if(!exist[s_begin]){//记录这个字母是否出现过
            fa[s_begin]=s_begin;
            total_u++;
            exist[s_begin]=1;
        }
        if(!exist[s_end]){
            fa[s_end]=s_end;
            total_u++;
            exist[s_end]=1;
        }
        if(s_begin!=s_end){//如果不是自环就连接
            if(get(s_begin)!=get(s_end)){
                merge(s_begin,s_end);
                total_u--;
            }
        }
        node tem;
        tem.to=s_end;
        tem.num=i;
        tem.a=s[i];
        ver[s_begin].push_back(tem);//建图
    }
    int ssum=0,fsum=0;
    for(int i=1;i<=26;i++){
        if(!exist[i]) continue;
        else if(rd[i]==cd[i]) continue;
        else if(rd[i]==cd[i]+1){
            fsum++;fx=i;
        }
        else if(rd[i]+1==cd[i]){
            sx=i;ssum++;
        }
        else{
            printf("***");
            system("pause");
            return 0;
        }
    }
    if(total_u!=1){
        printf("***");
        system("pause");
        return 0;
    }
    if(!((ssum==0&&fsum==0)||(ssum==1&&fsum==1))){
        printf("***");
        system("pause");
        return 0;
    }
    if(ssum==0&&fsum==0){
        sx=s[1][0]-'a'+1;
    }
    dfs(sx,0,0);
    system("pause");
    return 0;
}
posted @ 2025-02-24 23:02  elainafan  阅读(98)  评论(0)    收藏  举报