题目参考:
牛客网 vivo2020届春季校园招聘在线编程考试题
一根烟,一壶酒,一道算法编一宿。。。
这是个明显的图结构,九个结点,任意两个节点间相连,我们可以求出图的所有所有满足如下要求的有序路径:
1.路径长度在区间[m,n]内
2.路径经过的节点中,相邻节点的中点满足条件: 中点处无对应节点 或 对应节点已被访问过
用图来做实在是复杂。。光是求出所有有效路径这个问题都让我头大。。。
于是有了这个排列组合方法:
我们可以这样解决这个问题:
1.遍历 i in range(m,n+1)
2.遍历从9个节点中抽出i个节点的所有组合
3.遍历i个节点的所有排列,排列顺序即i个节点的访问顺序
4.判断是否满足中点条件
代码如下:
#define calMiddle(x,y) (x+y)/2
struct myPoint
{
int x, y;
};
class Solution {
public:
int solution(int m, int n) {
for (int i = m; i < n + 1; i++)
{
if (i == 0) {
result += 0;
continue;
}
if (i == 1) {
result += 9;
continue;
}
if (i == 2) {
result += 56;
continue;
}
if (i > 9)
continue;
//遍历所有组合并判断
combine(pointList, i);
}
//for (size_t i = m; i <= n; i++)
//{
// if (i > 9) {
// continue;
// }
// result += results[i];
//}
return result;
}
private:
void combine(const vector<myPoint>& targets, int k) {
if (k < 0) {
cout << "k < 0 in combine()" << endl;
exit(-1);
}
if (k == 0)
return;
vector<int> indexes;
for (int i = 0; i < k; i++)
{
indexes.push_back(i);
}
combineRecursion(targets, indexes);
return;
}
//组合的递归函数,实现了给定组合数的数组的遍历。
//通用于如下情景:需要从一个数组percents中抽取indexes.size()个数,对于每一种组合都要进行某一操作。
//percents:抽取组合对象列表
//indexes:抽取到的下标列表
void combineRecursion(const vector<myPoint>& targets, vector<int>& indexes) {
int targetsSize = targets.size(), indexesSize = indexes.size();
//最后一位是否到达终点
bool lastIsEnd = (indexes.back() == targetsSize - 1);
//如果最后一位未到达终点,则进行操作,更新参数,继续递归
if (!lastIsEnd) {
//--------------operation-----------------//
combineOperation(targets, indexes);
//--------------operation-----------------//
//更新参数
indexes.back()++;
//继续递归
combineRecursion(targets, indexes);
//子函数结束后,当前函数也返回
return;
}
//如果最后一位到达终点,则反向遍历,查询每一位是否到达终点
bool currentIsEnd;
for (size_t i = indexesSize - 2; i >= 0; i--)
{
currentIsEnd = (indexes[i] == targetsSize - indexesSize + i);
//如果上一位到达终点而当前位未到达终点,则进行操作,更新参数,继续递归
if (lastIsEnd && !currentIsEnd) {
//--------------operation-----------------//
combineOperation(targets, indexes);
//--------------operation-----------------//
//更新参数
indexes[i]++;
for (size_t j = i + 1; j < indexesSize; j++)
{
indexes[j] = indexes[j - 1] + 1;
}
//继续递归
combineRecursion(targets, indexes);
//子函数结束后,当前函数也返回
return;
}
//如果第0位也到达终点,则进行操作,结束递归
if (i == 0 && currentIsEnd) {
//--------------operation-----------------//
combineOperation(targets, indexes);
//--------------operation-----------------//
return;
}
}
}
void combineOperation(const vector<myPoint>& targets, vector<int>& indexes) {
//遍历所有排列,进行操作
permute(targets, indexes);
}
//给出一个数组,递归遍历该数组的所有排列
void permute(const vector<myPoint>& targets, vector<int> indexes) {
vector<int> indexes2 = indexes;
permuOperation(indexes);
while (prev_permutation(indexes.begin(), indexes.end())) {
permuOperation(indexes);
}
while (next_permutation(indexes2.begin(), indexes2.end())) {
permuOperation(indexes2);
}
}
//permute函数的操作函数
void permuOperation(const vector<int>& indexes) {
myPoint middle, point0, point1, prePoint;
bool flag0 = true, flag1 = false;//flag0--result,flag1--middle是否已按过的判断
//遍历所有中点
for (size_t i = 0; i < indexes.size() - 1; i++)
{
point0 = pointList[indexes[i]];
point1 = pointList[indexes[i + 1]];
flag0 = true;
//如果中点为整数
if ((point0.x + point1.x) % 2 == 0 && (point0.y + point1.y) % 2 == 0) {
//求中点
middle.x = calMiddle(point0.x, point1.x);
middle.y = calMiddle(point0.y, point1.y);
//遍历判断中点是否按过
for (size_t j = 0; j < i; j++)
{
prePoint = pointList[indexes[j]];
flag1 = false;
//如果按过
if (middle.x == prePoint.x&&middle.y == prePoint.y)
{
flag1 = true;
break;
}
}
//中点如果未按过,则当前情况不满足要求
if (!flag1) {
flag0 = false;
break;
}
}
}
if (flag0)
result++;
}
const vector<myPoint> pointList = { {0,0},{0,1},{0,2},
{1,0},{1,1},{1,2},
{2,0},{2,1},{2,2} };
const int pointListSize = pointList.size();
int result = 0;
int results[10] = { 0,9,56,320,1624,7152,26016,72912,140704,140704 };