赛后题解——问题 D: 游戏jienzi(记忆化)

注释:多组测试数据卡超时不多的话可以试试这题能否用数组记录,这样有的数据就可以 O ( 1 ) \small O(1) 时间算出

问题 D: 游戏jienzi
题目描述

Brother Jien 发明了一个有趣的游戏。
一开始你有一个数 S,接着呢,Jien 会按照自己的喜好给你 T 个 x,这时你要进行若干轮操作,第 i 轮(如果有)令 S−=x,之后让 x∗=pi。其中 pi是一个由你决定的,从二到九的一个整数。你要做的是用最少轮数令 S=0 。由于Jien 不保证对于每个 x 一定有解,所以无解时你应输出"Orz Brother Jien!"来表达你的敬意。

输入

第一行两个正整数 S , T ,含义如题所述。
接下来 T 行,每行一个数 x ,表示一个询问,由于 Jien 哥随性自由,所以保证由他出的数据在各个合法范围内纯随机生成。

输出

T 行,一行一个整数,表示最少的操作次数。
如果无解,请表达你对Brother Jien 的敬意。

样例输入

15 2
5
4

样例输出

2
Orz Brother Jien!

提示

对于 100 % \small 100\% 的数据 有 T 5 × 1 0 5 \small T≤5\times10^5 , x S 1 0 8 \small x≤S≤10^8

解决思路:由题意可以得到,有解的充要条件是 S = x + k 1 x + k 2 k 1 x + . . . + k n . . . k 2 k 1 x \small S=x+k_1x+k_2k_1x+...+k_n...k_2k_1x ,其中 2 k i 9 \small 2\leq k_i \leq 9
观察式子,发现只要 S \small S 不是 x \small x 的倍数,就必然无解。
考虑 S \small S x \small x 的倍数的情况,将式子两边都除以 x \small x
S x = 1 + k 1 + k 2 k 1 + . . . + k n . . . k 2 k 1 \small \therefore \large\frac{S}{x}\small=1+k_1+k_2k_1+...+k_n...k_2k_1
再进行变换,提出公因式
S x = 1 + k 1 ( 1 + k 2 ( . . . ( 1 + k n 1 ( 1 + k n ) ) . . . ) ) \small \therefore \large\frac{S}{x}\small=1+k_1(1+k_2(...(1+k_{n-1}(1+k_n))...))
这个式子就可以直接 d f s \small dfs 判断了,枚举 k i \small k_i ( 2 k i 9 ) \small (2\leq k_i \leq 9) ,枚举的时候如果这个 k i \small k_i n o w 1 \small now-1 的因子的话,就深搜记录最小值,否则不用管。
这道题的恶心之处在于多组测试数据,如果强行直接用 d f s \small dfs 计算的话只能过 90 % \small 90\% 数据,听了大佬的话,用一个数组将 2 e 7 \small 2e7 的数据记录下来,下次遇到直接输出就好了。

//优化
#pragma GCC optimize(2)
//C
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
//C++
#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<istream>
#include<iomanip>
#include<climits>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
//宏定义
#define N 1010
#define DoIdo main
//#define scanf scanf_s
#define it set<ll>::iterator
#define TT template<class T>
//定义+命名空间
typedef long long ll;
typedef unsigned long long ull;
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const int maxn = 2e7 + 10;
using namespace std;
//全局变量
int ans[maxn];
int mi = INT_MAX;
//函数区
ll max(ll a, ll b) { return a > b ? a : b; }
ll min(ll a, ll b) { return a < b ? a : b; }
inline ll read() {
	ll c = getchar(), Nig = 1, x = 0;
	while (!isdigit(c) && c != '-')c = getchar();
	if (c == '-')Nig = -1, c = getchar();
	while (isdigit(c))x = ((x << 1) + (x << 3)) + (c ^ '0'), c = getchar();
	return Nig * x;
}
inline void dfs(int now, int cnt) {
	//当我的数减到0时记录最小值并跳出循环
	if (now == 0) {
		if (mi > cnt) mi = cnt;
		return;
	}
	//枚举所有可能的系数
	for (int i = 2; i <= 9; i++) {
		//只有i是now - 1的因子时才可能有解
		if ((now - 1) % i == 0) {
			dfs((now - 1) / i, cnt + 1);
		}
	}
}
#define read read()
//主函数
int DoIdo() {

	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);

	int S = read;
	int T = read;

	while (T--) {
		int x = read;

		//如果之前有过询问,直接判断数组的值就好
		if (x <= 2e7 && ans[x] != 0) {
			if (ans[x] == INT_MAX) printf("Orz Brother Jien!\n");
			else printf("%d\n", ans[x]);
			//千万别忘记跳过此次询问
			continue;
		}
		//如果x不是S的因子,肯定无解
		if (S % x) {
			printf("Orz Brother Jien!\n");
			//对于小于2e7的数据,记录到数组里
			if (x <= 2e7) ans[x] = INT_MAX;
		}
		else {
			mi = INT_MAX;
			//进行dfs判断是否有解,传入参数要除以x
			dfs(S / x, 0);
			//对于小于2e7的数据,记录到数组里
			if (x <= 2e7) ans[x] = mi;
			//对于没有更新mi直接输出无解就好
			if (mi != INT_MAX) printf("%d\n", mi);
			else printf("Orz Brother Jien!\n");
		}
	}
	return 0;
}
//分割线---------------------------------QWQ
/*


*/

猜你喜欢

转载自blog.csdn.net/qq_45739057/article/details/106366716