持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情。
连接奶牛
题目描述
每天农夫约翰都会去巡视农场,检查他的 NN 头奶牛的健康状况。
每头奶牛的位置由二维平面中的一个点描述,而约翰从原点 (0,0)(0,0) 出发。
所有奶牛的位置互不相同,且都不在原点。
为了使自己的路线更有趣,农夫约翰决定只沿平行于坐标轴的方向行走,即只能向北,向南,向东或向西行走。
此外,他只有在到达奶牛的位置时才能改变行进方向(如果需要,他也可以选择通过奶牛的位置而不改变方向)。
在他改变方向时,可以选择转向 9090 或 180180 度。
约翰的行进路线必须满足在他访问完所有奶牛后能够回到原点。
如果他在每头奶牛的位置处恰好转向一次,请计算出约翰访问他的 NN 头奶牛可以采取的不同路线的数量。
允许他在不改变方向的情况下通过任意奶牛的位置任意次数。
同一几何路线正向走和反向走算作两条不同的路线。
输入格式
第一行包含整数 NN。
接下来 NN 行,每行包含两个整数 (x,y)(x,y) 表示一个奶牛的位置坐标。
输出格式
输出不同路线的数量。
如果不存在有效路线,则输出 00。
数据范围
1≤N≤101≤N≤10, −1000≤x,y≤1000−1000≤x,y≤1000
输入样例:
4
0 1
2 1
2 0
2 -5
输出样例:
2
样例解释
共有两条不同路线 1−2−4−31−2−4−3 和 3−4−2−13−4−2−1。
思路
对所有奶牛的编号作全排列,如果某个排列满足以下要求,答案加一:
存在路径 (0, 0) -> 某排列 -> (0, 0) 在每头奶牛的位置处恰好转向一次 全排列复杂度 O(n!)O(n!),检测复杂度 O(n)O(n),总时间复杂度 O(nn!)≤4×107, n≤10O(nn!)≤4×107, n≤10 全排列请回顾基础课 AcWing 842. 排列数字 判断下个点是否在特定方向请回顾每日一题 AcWing 1986. 镜子
代码
#include <iostream>
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;
const int N = 20;
int n, ans;
int path[N];
PII cow[N];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};\
int get_d(int a, int b) {
auto [ax, ay] = cow[path[a]];
auto [bx, by] = cow[path[b]];
int d = -1;
for (int k = 0; k < 4; k ++)
if (ax + abs(ax - bx) * dx[k] == bx &&
ay + abs(ay - by) * dy[k] == by) {
d = k;
break;
}
return d;
}
bool check() {
// 1. 存在路径 (0, 0) -> path[1, n] -> (0, 0)
// 2. 每头奶牛的位置处恰好转向一次
for (int i = 1, lastd = -1; i <= n + 1; i ++) {
int d = get_d(i - 1, i);
if (d == -1 || d == lastd) return 0;
lastd = d;
}
return 1;
}
void dfs(int u, int s) {
if (u > n) {
if (check()) ans ++;
return;
}
for (int i = 1; i <= n; i ++) {
if (s >> i & 1) continue;
path[u] = i;
dfs(u + 1, s | 1 << i);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> cow[i].x >> cow[i].y;
dfs(1, 0);
cout << ans << endl;
return 0;
}