这题是我觉得前三小节里最难的题了,没有之一。
题目出的还是挺好的,至少能够锻炼recursion的能力。
感觉纯粹就是DFS+回溯的方法。
题目概述:
这道题是说农夫的院子里面出现了一些虫洞,虫洞嘛就是从一个出来就到了另一个地方,虫洞总是两两配对且随机组合,且位置由x,y,z坐标给出。
现在已知有一只只会沿着正x方向行走的奶牛行走于虫洞之间,那么我们要计算在给定地图下,对于不同的虫洞配对组合,这只奶牛有没有可能陷入死循环,如果有的话,共有多少种可能会使其进入死循环。
算法思想:
这道题我是看了答案的,有两个点自己没有想出来。
1.数据量很小,所以一定是枚举就可以,但是如何枚举?(partner数组)
2.如何判断是否有loop。(r数组)
后来发现枚举的方法即是一种类似于回溯的方法,像极了之前所做的dfs+回溯的策略,就是检测第一个未配对的虫洞,把他和后面的配对起来,然后用递归的方法再进行一次配对,因为全局变量的优势,改动会被保存。这样在运行到一定次数后,所有都会进行配对,这是在进行是否有死循环的检查就好了。
当然每次检查之后都要把配对双方重新设为0,以此进行枚举。
至于如何判断是否有循环,即是进行n-1次位置移动,每一次位置移动都是先看其出来的位置是不是0。因为在我的程序中,r[i] 代表了 i 的右边距离最近的点的序号,如果是0说明奶牛已经走出。且同时r[0]也等于0,如果已经走出就不会再回来。
另外有一个小思考,我在写到这里的时候突然想,既然每次跳跃都是先找partner再往右移,也就是走过了两个点,那么岂不是n/2步就可以而不用n,试了一下发现不行。应该是因为这样无法遍历所有的情况。无法从所有点进入一次。
代码部分:
#include <iostream> #include <list> #include <map> #include <math.h> #include <string.h> #include <string> #include <fstream> #include <algorithm> using namespace std; ifstream fin("wormhole.in"); ofstream fout("wormhole.out"); int n, res; int partner[14]; int x[14], y[14], r[14]; bool cycle_exist() { for (int i = 1; i <= n; i++) { int pos = i; for (int j = 1; j < n; j++) { pos = r[partner[pos]]; } if (pos != 0) return true; } return false; } int solve() { int i; int total = 0; for (i = 1; i <= n; i++) { if (partner[i] == 0) break; } if (i >= n) { if (cycle_exist()) return 1; else return 0; } for (int j = i+1; j <= n; j++) { if (partner[j] == 0) { partner[i] = j; partner[j] = i; total += solve(); partner[i] = partner[j] = 0; } } return total; } int main() { fin >> n; for (int i = 1; i <= n; i++) { fin >> x[i] >> y[i]; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (x[j] > x[i] && y[j] == y[i]) { if (r[i] == 0 || x[j] - x[i] < x[r[i]] - x[i]) { r[i] = j; } } } } fout << solve() << endl; return 0; }