先看题目:https://ac.nowcoder.com/acm/problem/207754
题目描述:
牛牛参观景点,任意两个景点间都有路相连,牛牛希望经过某些路,为了参观完所有景点并且每个景点只参观一次,有多少种方法?
解题思路:
显然,如果A-B,B-C,C-A都要走即A、B、C成环了,那么A必然要经过两次,显然不行。这道题怎么考虑呢?举个例子试试:
图片说明
如图,
(1,2)要走,(4,5)要走,(6,7)要走,(7,8)要走,(7,9)要走
发现,如果某个点的度大于2,显然也是不行的,比如7无论如何都会经过2次
如果(7,9)这条边不存在,还是从1-9,我们来思考下总共有多少种方法?
把每个集合看成一个整体,那么整体上进行排序就是5!
然后每个集合都可以从左往右或从右往左(无向),这样的话就是2^3
故答案就是5!* 2^3种方法。
编程实现上,可以通过并查集来判断无向图的成环。详见:
https://blog.nowcoder.net/n/5ef13c1f51074a65815569d278dd63f9
并查集实现集合的合并查找等操作。
更多细节上的注意,见代码注释
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
char s[60];
int fa[60];
int num[60];

int found(int x)
{
    return fa[x] == x ? x : fa[x]=found(fa[x]);
}

int main()
{
    int n;
    ll ans = -1;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    for(int i = 1; i <= n; i++)
    {
        scanf("%s", s+1);
        int du = 0;
        for(int j = 1; j <= n; j++) {    //注意j是到n而不是j<i就结束了,因为要判断度,容易忘
            if(s[j] == 'Y') {
                if(++du > 2) ans = 0;
                if(i < j) {
                    if(found(i) == found(j)) ans = 0;
                    else fa[fa[i]] = fa[j];    //上面found已经路径压缩完了,因此可以这样写
                }
            }
        }
    }
    if(!ans)
        printf("0\n");
    else {
        ans = 1;
        int s = 0;
        for(int i = 1; i <= n; i++) {    //查找每个集合的元素个数,方法是:遍历一遍所有元素,每个元素都找到它的祖先,num[祖先]++    
            num[found(i)]++;
            if(fa[i] == i){
                s++;
            }
        }
        for(int i = 1; i <= s; i++) ans = ans * i % mod;
        for(int i = 1; i <= n; i++) {
            if(num[i] > 1) ans = ans * 2 % mod;
        }
        printf("%lld\n",ans);
    }
}