算法题(连接奶牛)

180 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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;
}