题目:UVa1583
地址:https://vjudge.net/problem/UVA-1583
题解:
如果x加上x的各个位上的数字后得到y,就说x是y的生成元。给出n(1<=n<=100000),求最小生成元(若没有生成元,就输出0)。例如,n=216,输出198,n=121,输出0。
思路:
对于一个n,不难发现它的生成元肯定是小于n,那么知道它的生成元的范围后,可以直接暴力枚举呀。
不过问题来了,一个n,你可以直接枚举,但是如果有很多组n呢,而且n最大可以为100000,只要1000个这个最大量级的输入,就有可能会超时。
可以想到制表,可以把100000以内的数事先算出来,制成表(table[i]=sum表示i是sum的生成元),然后要输出时,依次枚举1~n的table值,找出第一个table值等于n的i(即最小生成元),但是你会发现,依旧会超时,尽管节省了算生成元的时间,但是每次输出的时间复杂度依旧是O(n),那么可不可以优化输出时的复杂度呢,答案是肯定的。
只要在制表的时候将值和下标颠倒一下就可以了,即table[sum]=i,这样sum的生成元就是table[sum],可以直接输出,复杂度降到了O(1)!!!
编程技巧:
① 对于会重复用到的且可以事前计算的,可以在输入之前,提前制表。
注意点:
① 由于题目要求的是最小生成元,所以在制表时,就得注意要把最小的生成元放进table数组中。if(!table[sum]||sum<table[sum]) table[sum] = i;
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
int T;
int table[100005];
void Init(){ ///预处理,制表
memset(table,0,sizeof(table)); ///初始化为0,这样就保证了没有生成元的数将输出0
for(int i = 0;i <= 100005;++i){
int x = i;
int sum = 0;
while(x){
sum += x%10;
x /= 10;
}
sum += i; ///i是sum的生成元
if(!table[sum]||sum<table[sum]) ///其中sum<table[sum]一句是为了保证table[sum]中放的是sum最小的生成元
table[sum] = i;
}
}
int main(){
Init();
cin >> T;
while(T--){
int n;
cin >> n;
cout << table[n] << endl; ///直接输出答案
}
return 0;
}