zoukankan      html  css  js  c++  java
  • sgu 101 domino

        题意还算简洁明了,加上有道翻译凑过着读完了题。题意大体上是 给你 个多米诺骨牌, 给出每个骨牌两端的数字, 只有数字相同才可以推到, 比如 2-3和3-2。你可以旋转这些多米诺骨牌, 输出一个可以全部推到的方案, 如果没有 ,输出 No solution。

        第一眼看上去像爆搜, 但是 n 最大到100, 时限竟然只有0.25s,铁定超时, 换个思路, 想不出来, 看了题解,才发现原来是图论题,我们把 0~6 当做点,把每个骨牌当做边, 这样构成了一个图, 我们需要求得就是 遍历所有的边且不重复

        这个可以算是一个 欧拉路 模板题,注意,是 欧拉路, 不是欧拉回路 ,被坑了好久。

        欧拉路和欧拉回路都是一笔画问题, 两者都需要满足一个必要条件 : 度数为奇数的要么没有, 要么有2个。 度数就是这个点连得边的条数。

        先说欧拉回路, 欧拉回路由于需要回到原点, 所以一定没有度数为奇的点, 只要从任意一点 dfs ,走过的边不再走, 直到无边可走, 就是欧拉回路,此时一定是在原点。

        但是,欧拉路不同, 欧拉路可以不回到原点, 这导致 dfs 有可能导致死胡同 , 看一个例子 :

    在这个例子里, 如果从 3 开始 dfs, 我们有可能回走到 2 ,然后走到 1, 这时我们发现无路可走了, 但这本应是一个一笔画, 只要从 1 出发就可以了,但是我们在程序里不好判断从哪个点开始, 所以, 引入欧拉路的求法:

        从任意一个度数为奇的点开始,仍然 dfs,但是 我们不一开始就把这个点加入答案, 而是先任选和这个点相连的一条边, 继续向下dfs, 然后在把这个店加入答案,就等于是倒序输出。这样很巧妙的解决了上面说的问题。光这么说可能不太好理解,一看代码立刻就能明白。

    void ss(int now)
    {
        遍历每一条和这个点相连的边
    { 标记已经走过这条边,以后不再走 ss(和它相邻的点); 把现在这个点加入答案 } }

        如此一来就可以了, 但要注意,要判断此图是否联通, 只需判断你求出的边数和骨牌数是否相等就行了,上代码:

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #define N 110 * 2
    #define M 10
    using namespace std;
    
    int n, du[M] = {0};
    int p[M], next[N*2], v[N*2], zheng[N*2], bnum = -1, kexing[N*2], num[N*2];
    int ans[N][2], ansnum = 0;
    
    void addbian(int x, int y, int now)
    {
        bnum++; next[bnum] = p[x]; p[x] = bnum;
        v[bnum] = y; zheng[bnum] = 1; kexing[bnum] = 1; num[bnum] = now;
        bnum++; next[bnum] = p[y]; p[y] = bnum;
        v[bnum] = x; zheng[bnum] = 0; kexing[bnum] = 1; num[bnum] = now;
    }
    
    void ss(int now)
    {
        int k = p[now];
        while (k != -1)
        {
            if (kexing[k])
            {
                kexing[k] = 0; kexing[k^1] = 0;
                ss(v[k]);
                ansnum++;
                ans[ansnum][0] = num[k^1];
                ans[ansnum][1] = zheng[k^1];
            }
            k = next[k];
        }
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 0; i <= 6; ++i) p[i] = -1;
        for (int i = 1; i <= n; ++i)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            du[x]++; du[y]++;
            addbian(x, y, i);
        }
        if (n == 0)
        {
            printf("No solution
    ");
            return 0;
        }
        int jnum = 0, start = -1;
        for (int i = 0; i <= 6; ++i)
            if (du[i] % 2 != 0)
            {
                jnum++; start = i;
            }
        if (jnum !=0 && jnum != 2)
        {
            printf("No solution
    ");
            return 0;
        }
        if (start == -1)
            for (int i = 0; i <= 6; ++i)
                if (du[i] != 0) start = i;
        ss(start);
        if (ansnum < n)
        {
            printf("No solution
    ");
            return 0;
        }
        for (int i = 1; i <= n; ++i)
        {
            printf("%d ",ans[i][0]);
            if (ans[i][1]) printf("+
    ");
            else printf("-
    ");
        }
    }
  • 相关阅读:
    PHP中echo和print的区别
    Python input和raw_input的区别
    for,if语句
    Mac下安装PEAR
    IOS之代理
    socket总结
    jQuery 遍历函数 ,javascript中的each遍历
    DP:最大公共子序列
    6.2省赛总结
    NEUOJ1302最大子序列
  • 原文地址:https://www.cnblogs.com/handsomeJian/p/3729904.html
Copyright © 2011-2022 走看看