USACO 1.3.5 Wormholes

这题是我觉得前三小节里最难的题了,没有之一。

题目出的还是挺好的,至少能够锻炼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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值