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: zachery1
PROG: wormhole
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <cstring>
#include <algorithm>
#define MAXN 20
#define cin fin
#define cout fout
using 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;
}