USACO-Section1.3 Wormholes [搜索]

来源:互联网 发布:外星文明等级 知乎 编辑:程序博客网 时间:2024/06/06 01:15

2017-6-1

题目大意

农夫约翰爱好在周末进行高能物理实验的结果却适得其反,导致农场上产生了N个虫洞(2<=N<=12,n是偶数),每个在农场二维地图的一个不同点。
根据他的计算,约翰知道他的虫洞将形成 N/2 连接配对。例如,如果A和B的虫洞连接成一对,进入虫洞A的任何对象体将从虫洞B出去,朝着同一个方向,而且进入虫洞B的任何对象将同样从虫洞A出去,朝着相同的方向前进。这可能发生相当令人不快的后果。
例如,假设有两个成对的虫洞A(1,1) 和 B(3,1),贝茜从(2,1)开始朝着 +x 方向(右)的位置移动。贝茜将进入虫洞 B(在(3,1)),从A出去(在(1,1)),然后再次进入B,困在一个无限循环中!

| … .
| A > B . 贝茜会穿过B,A,
+ … . 然后再次穿过B

农夫约翰知道他的农场里每个虫洞的确切位置。虽然他不记得贝茜的当前位置,但是他知道贝茜总是向着 +x 的方向走。请帮助农夫约翰计算不同的虫洞配对(情况),使贝茜可能从不幸的位置开始被困在一个无限循环中。(copy from nocow)

题解

这题分为两个步骤:
一、找出所有的配对情况。
二、判断当前配对是否能构成无限循环。

枚举所有的配对情况,可以用dfs+回溯实现。dfs(step)每次搜索,都将匹配一对虫洞,搜索深度最大为n/2。step=n/2时表示已经配对了n/2对,已经找到一种配对情况。我们每次先找到一个未被标记的虫洞s,然后再枚举所有其他的未被标记的虫洞v来与s配对。
判断无限循环,枚举所有可能的开始位置,然后判断经过虫洞的次数之和是否超过了2n。超过2n则说明陷入了循环。

代码

/*ID: zachery1PROG: wormholeLANG: C++*/#include <iostream>#include <fstream>#include <cstring>#include <algorithm>#define MAXN 20#define cin fin#define cout foutusing namespace std;ifstream fin("wormhole.in");ofstream fout("wormhole.out");int n, ans = 0;pair<int, int> wormholes[MAXN];int next[MAXN], pairs[MAXN];bool visit[MAXN];// 判断无限循环bool isCycle() {    for (int i = 0; i < n; i++) {        int v = i;        int cnt = 0;        while (true) {           if (cnt >= 2*n) return true;// 感谢dyt同学找出的bug,                                       // 起初认为判断cnt >= n即可, 但 2 1 2 1就是一个反例           v = pairs[v];           if (next[v] == 0) break;           v = next[v];           cnt++;        }    }    return false;}void dfs(int step) {    if (step == n / 2) {        if(isCycle()) {            ans++;        }    }    else {        //先找一个未被标记的s        int s = 0;        for (int i = 0; i < n; i++) {            if (!visit[i]) {                s = i;                visit[s] = true;                break;            }        }        //再枚举所有的未被标记的黑洞,与之配对,继续搜索        for (int i = 0; i < n; i++) {            if (!visit[i]) {                visit[i] = true;                pairs[i] = s;                pairs[s] = i;                dfs(step+1);                visit[i] = false;//回溯            }        }        visit[s] = false; //回溯    }}int main() {    ios::sync_with_stdio(false);    cin >> n;    memset(next, 0, sizeof(next));    memset(wormholes, 0, sizeof(wormholes));    for (int i = 0; i < n; i++) {        // 对pair<int, int>用sort排序,默认是先按照first,在按照second排序        // 所以 second 为 x, first 为 y        cin >> wormholes[i].second >> wormholes[i].first;    }    // 先按y轴,再按x轴排序  (谢谢认真看我代码的ljj给我纠正。)    // 这样同一行的黑洞(y相同,x不相同)排序后序号就是相邻的,方便寻找next[]    sort(wormholes, wormholes+n);    for (int i = 0; i < n-1; i++) {        //next[i] 就是右边的第一个黑洞,或者出界,用0表示        if (wormholes[i+1].first == wormholes[i].first) {            next[i] = i+1;        }    }    dfs(0);    cout << ans << endl;    return 0;}
原创粉丝点击