Getting in Shape

大佬同学推荐的一道题,确实折腾了有够久,之后在一个问题上面卡了一年..

Juan decided to start working out and is willing to prepare a workout session.

He knows that some days he might not want to do all exercises from his workout session. He decided on some rules to avoid skipping the whole session and not exercising at all, while still allowing him to optionally skip some exercises.

The rules are:

  • There will be only two types of exercises: AA and BB.
  • After finishing an exercise of type BB he moves to the next exercise, if there is one. Otherwise, the workout session ends.
  • After finishing an exercise of type AA there are two possibilities: he can move to the next exercise, or he can skip the next exercise and move to the one after that.
  • The last exercise in a workout session must always be of type BB.

Therefore, there might be different ways in which the workout session can be completed. For example, if the types of exercises in a workout session are BAABBAAB, there are 3 ways in which the session can be completed: by doing all exercises, by skipping the 3rd one or by skipping the last one.

Juan wants to prepare his workout session in such a way that there are exactly NN different ways in which the workout session can be completed. Can you help him?

Input

One positive integer NN (2≤N≤10152≤N≤1015), representing the number of ways in which the workout session can be completed.

Output

Output a line containing a string, formed only with characters 'A' and 'B', representing the types of the exercises in the workout session. If there are multiple valid answers, output the lexicographically smallest answer. If there is no valid workout sessions, output a line containing the string "IMPOSSIBLE" (without quotes).

        概括一下,题目需要找出一个以B结尾,并且只包含AB两种字符,同时按照规则的走法一共是n的字符串,并且需要在所有合法的字符串中字典序最小。

扫描二维码关注公众号,回复: 15914650 查看本文章

        假设最终字符串长度为length,这道题可以抽象为到第length+1位置的走法为n。考虑到达任意位置i(i>2)的走法总数,其实只与前面两位有关,也就是num[i]=num[i-1]+num[i-2],非常类似于斐波那契数列,但是不同的是如果i-2位置是B,那么其实这时候num[i-2]的方法数是不能叠加到num[i]的,也就是num[i]=num[i-1](如果i-2位置是B)。

         到了这里我们应该可以看出,B其实是比较特殊的,多个连续的B实际上并不会使得方法数增加,只是简单的向下传递方法数,因此B再多等同于一个B,为了字典序最小,只用考虑一个加入了一个B会发生什么。

        观察A...AB(1)A...AB(2)A...,连续的A就是不断累前两个位置的路线数量,到了B(1)后面的A,这个A的路线数量实际上就是前面的A...AB(1)串的总的路线数量,然后这些路线全部到达B(1)后面的A身上,然后以这些路为起点再出发向下传递,到了B(2)后面的A其实就是前面两个A..AB串的路线数的乘积,因为你怎么到我B(1)后面A的我不管,但是这每一条路线都会对应在A...AB(2)所有路线,符合乘法原则,更多组其实也是一个道理,就是将每一串A...AB单独的路线数相乘,而每一个A...AB串就代表了斐波那契数列的一个数字。

        这时候解决问题的思路就差不多出来了,只要判断一个数能不能表达成斐波那契数列中的数字的乘积就可以。为了字典序最大,还应当从大的开始查找。一开始我是用了贪心的算法,从最大去寻找,用循环从大到小查找解决,但是后来发现不行,考虑用dfs查找,也就是找到了因子,然后就这种情况继续查找,一直找到最后可以了退出。

        记录一个小问题(就是一直wa的点),就是dfs的答案数列长度cnt+1一定要放在dfs里面,因为放在外面在循环的时候可能找到了大的因子,但是最后证明不可以,也就是加了大因子最后没答案,但是小因子可以(这其实也是贪心为什么不可以的原因),是要去找小的因子组合的,因为优先要有答案,但是你cnt在查找的循环里面加了,到时候找到答案就会继续在后面叠加。总结就是:需要回溯的一定要放在dfs的参数里面,这样后面找着找着不可以了,才能方便回到前面状态。

下面是代码:

#include<stdio.h>
#define ll long long
ll a[1000];
int ans[1000];//答案
int stats = 0;//状态

void dfs(ll sr, int cnt, int k) {
	if (sr == 1) {
		for (int i = 1; i <= cnt-1; i++) {
			for (int j = 1; j <= ans[i] - 1; j++) {
				printf("A");
			}
			printf("B");
		}
		printf("\n");
		stats = 1;//找到答案了
		return;
	}
	for (int i = k; i >= 3; i--) {
		if (sr%a[i] == 0) {
			k = i;
			ans[cnt] = i - 1;//到时候A...B串的长度
            //cnt++; //必须要放到里面去,不然当前情况不行,后面可以了,但是cnt就是这个数字了
			dfs(sr/a[i], cnt+1, k);
			if (stats) {
				return;
			}
			//printf("n=%lld *a[i]=%d\n",n, a[i]);
		}
	}
}

int main() {
	ll n; scanf("%lld", &n);
	a[1] = 1;
	a[2] = 1;
	int k;
	for (int i = 3; i <= 1000; i++) {
		a[i] = a[i - 1] + a[i - 2];
		if (a[i] > n) {
			k = i - 1;
			break;
		}
	}
	//printf("%d\n", num);
	int cnt = 1;//答案长度
	dfs(n, cnt, k);
	if (!stats) {
		printf("IMPOSSIBLE\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_60360239/article/details/127207114