• POJ


    https://vjudge.net/problem/POJ-2513

    题解转载自:優YoU  http://user.qzone.qq.com/289065406/blog/1304742541

    题意

    给定一些木棒,木棒两端都涂上颜色,求是否能将木棒首尾相接,连成一条直线,要求不同木棒相接的一边必须是相同颜色的。

    分析

    可以用图论中欧拉路的知识来解这道题,首先可以把木棒两端看成节点,把木棒看成边,这样相同的颜色就是同一个节点

    问题便转化为:

    给定一个图,是否存在“一笔画”经过涂中每一点,以及经过每一边一次。

    这样就是求图中是否存在欧拉路Euler-Path。

    回顾经典的“七桥问题”,相信很多同学马上就明白了什么是 欧拉路 了,这里不多作解释。

    由图论知识可以知道,无向图存在欧拉路的充要条件为:

    ①     图是连通的;

    ②     所有节点的度为偶数,或者有且只有两个度为奇数的节点。

    其中①图的连通性用程序判断比较麻烦,先放一下。

    这里先说说②关于度数的判断方法:每种颜色出现的次数即为对应结点的度数。

    证明①图的连通性,使用并查集MergeSet是非常高效的方法。

    基本方法:

    初始化所输入的n个结点为n棵树,那么就有一个n棵树的森林,此时每棵树的有唯一的结点(根),该结点的祖先就是它本身。再通过不断地输入边,得到某两个结点(集合)之间的关系,进而合并这两个结点(集合),那么这两个集合就构成一个新的集合,集合内的所有结点都有一个共同的新祖先,就是这个集合(树)的根。

    最后只要枚举任意一个结点,他们都具有相同的祖先,那么就能证明图时连通的了。

    但是单纯使用并查集是会超时的,因为这样会导致每次寻找某个结点的祖先时,平均都会花费O(n/2)时间,最坏情况,当n==50W时,O(n/2)大概为25ms,那么要确定50W个结点是否有共同祖先时,总费时为50W*25ms 。

    因此必须使用并查集时必须压缩路径,前几次搜索某个结点k的祖先时,在不断通过父亲结点寻找祖先结点时,顺便把从k到最终祖先结点S中经过的所有结点的祖先都指向S,那么以后的搜索就能把时间降低到O(1)。

     由于并查集必须利用 数组的下标 与 存储的对象,使用int是比较方便的处理方法,但是题目的“颜色结点”是string,不方便用来使用并查集,即使用map也不行,虽然STL的map是基于hash的基础上,但并不高效,在本题中使用会超时。

    为此可以使用Trie字典树,得到每个颜色单词对应的int编号id ,可以说利用Trie把string一一映射到int,是本题后续处理的关键所在。下面使用了动态创建字典树的方法。PS:注意创建TrieTree链表时,C++不存在NULL,要用 0 替代 NULL

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<set>
    #define rep(i,e) for(int i=0;i<(e);i++)
    #define rep1(i,e) for(int i=1;i<=(e);i++)
    #define repx(i,x,e) for(int i=(x);i<=(e);i++)
    #define X first
    #define Y second
    #define PB push_back
    #define MP make_pair
    #define mset(var,val) memset(var,val,sizeof(var))
    #define scd(a) scanf("%d",&a)
    #define scdd(a,b) scanf("%d%d",&a,&b)
    #define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define pd(a) printf("%d
    ",a)
    #define scl(a) scanf("%lld",&a)
    #define scll(a,b) scanf("%lld%lld",&a,&b)
    #define sclll(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
    #define IOS ios::sync_with_stdio(false);cin.tie(0)
    
    using namespace std;
    typedef long long ll;
    template <class T>
    void test(T a){cout<<a<<endl;}
    template <class T,class T2>
    void test(T a,T2 b){cout<<a<<" "<<b<<endl;}
    template <class T,class T2,class T3>
    void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;}
    template <class T>
    inline bool scan_d(T &ret){
        char c;int sgn;
        if(c=getchar(),c==EOF) return 0;
        while(c!='-'&&(c<'0'||c>'9')) c=getchar();
        sgn=(c=='-')?-1:1;
        ret=(c=='-')?0:(c-'0');
        while(c=getchar(),c>='0'&&c<='9') ret = ret*10+(c-'0');
        ret*=sgn;
        return 1;
    }
    //const int N = 1e6+10;
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x3f3f3f3f3f3f3f3fll;
    const ll mod = 1000000000;
    int T;
    
    void testcase(){
        printf("Case %d:",++T);
    }
    
    const int MAXN = 5e5+5 ;
    const int MAXM = 550;
    const double eps = 1e-8;
    const double PI = acos(-1.0);
    
    class TrieTree_Node{
    public:
        bool flag;   //标记到字典树从根到当前结点所构成的字符串是否为一个(颜色)单词
        int id; //当前颜色(结点)的编号
        TrieTree_Node* nxt[27];
    
        TrieTree_Node(){//初始化
            flag = false;  
            id=0;    
            mset(nxt,0);
        }
    }root;//字典树根节点
    
    int color=0;     //颜色编号指针,最终为颜色总个数
    int degree[MAXN]={0}; //度数
    int ancestor[MAXN]; //祖先
    
    int Find(int x){
        //路径压缩
        return ancestor[x]==x?ancestor[x]:ancestor[x]=Find(ancestor[x]);
    }
    
    void Union(int a,int b){
        int pa=Find(a);
        int pb = Find(b);
        ancestor[pb]=pa;  //使a的祖先 作为 b的祖先
    }
    
    //利用字典树构造字符串s到编号int的映射
    int Hash(char *s){
        TrieTree_Node* p=&root; //从TrieTree的根节点出发搜索单词(单词不存在则创建)
        int len=0;
        while(s[len]!=''){
            int index = s[len++]-'a'; //把小写字母a~z映射到数字的1~26,作为字典树的每一层的索引
            if(!p->nxt[index]) p->nxt[index]=new TrieTree_Node;  //当索引不存在时,构建索引
            p=p->nxt[index];
        }
        if(p->flag){ //颜色单词已存在
            return p->id;
        }else{//否则创建单词
            p->flag=true;
            p->id=++color;
            return p->id;
        }
    }
    int main() {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
        for(int i=1;i<MAXN;i++) ancestor[i]=i;//初始化,每个结点作为一个独立集合
        char a[11],b[11];
        while(cin>>a>>b){
            int i = Hash(a);
            int j = Hash(b);
            degree[i]++;
            degree[j]++;
            Union(i,j);
        }
        int s=Find(1);
        int num=0;
        for(int i=1;i<=color;i++){
            if(degree[i]&1) num++;
            if(num>2||Find(i)!=s){
                puts("Impossible");
                return 0;
            }
        }
        if(num==1) puts("Impossible");
        else puts("Possible");
        return 0;
    }
  • 相关阅读:
    修改大表结构注意事项
    短信猫 Mysql ODBC问题
    各互联网公司UDE分享
    MySQL 取分类后的前n条
    批处理当前日期
    Fixed Event Scheduler No data zero rows fetched, selected, or processed
    数据库设计注意事项
    本月,下一月, 上一月 的 1号, 最后一号
    自动化测试编程语言的选择
    QTP描述编程中使用正则表达式
  • 原文地址:https://www.cnblogs.com/fht-litost/p/9246527.html
Copyright © 2020-2023  润新知