CodeFoeces GYM 101466A Gaby And Addition (字典树)
题目分析
题意:
给出n个数,找任意两个数 “相加”,求这个结果的最大值和最小值,注意此处的加法为不进位加法。
思路:
由于给出的数最多有 1e6 个,且每个数的值最大为 1e18 ,又因为特殊的加法运算,我们自然无法用常规的方法解决
注意到这个加法运算可以分配到每一位上进行运算,而且最大为1e18,十九位数,那么我们就可以用字典树来存储每个数,并进行计算,为了将字典树每个结点的深度和数的位数对应起来,我们可以将每个数都处理为19位数,从高位依次存入字典树,这样一来,就方便我们求最值了。
然后就是求最值的问题,我最初的想法是先将所有数存入字典树,随后枚举字典树的每条链(从根结点到叶子节点的路径),以求出最值,不过TLE了,想来这样写确实时间比较长,最坏的情况下查询用时:5 ^ 18 次,大约3.8e13次运算, 妥妥地TLE。不过我们可以在插入某个数之前,求出这个数和字典树中存在的数“相加”的得到的最值,随后将这个数存入字典树,最后所有数存入字典树后,就可以得到最值了。
总结:
这个题目写了我近半天,确定是字典树后,由于求最值这里对原方法用时估计错误,导致自己一直TLE在第四组样例,后来请教同学才发现问题。
还有就是这个地方求最值的时候不要用递归,在这里递归的效率很低。
代码区
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<string> #include<fstream> #include<vector> #include<stack> #include <map> #include <iomanip> #define bug cout << "**********" << endl #define show(x,y) cout<<"["<<x<<","<<y<<"] " //#define LOCAL = 1; using namespace std; typedef long long ll; typedef unsigned long long ull; unsigned const long long inf = (ull)10000000000000000000; const int mod = 1e9 + 7; const int Max = 2e7 + 10; struct Node { int maps[10]; }node[Max]; int n,id; ull num[22]; ull max_val, min_val; char str[45], str2[45]; char lead[][19]{ "","0","00","000","0000","00000" ,"000000","0000000","00000000","000000000", "0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000", "00000000000000000","000000000000000000"}; //用于补充前导零,使得数凑成19位数 void insert() //字典树常规插入操作 { int root = 0; for (int i = 0;i < 19;i++) { if (node[root].maps[str[i] - '0'] == 0) node[root].maps[str[i] - '0'] = ++id; root = node[root].maps[str[i] - '0']; } } ull dfs(int mode) //mode == 1代表求最大值 { ull val = 0; int root = 0; for (int i = 0; i <= 18;i++) //最大19位数 { int id = -1; ull t = 0; //记录这个位置上的最大(小)值 if (mode == 0) t = 9; for (int j = 0;j <= 9;j++) { if (mode && node[root].maps[j] && (j + str[i] - '0') % 10 >= t) { id = j;t = (j + str[i] - '0') % 10; } else if (!mode && node[root].maps[j] && (j + str[i] - '0') % 10 <= t) { id = j;t = (j + str[i] - '0') % 10; } } val += num[i] * t; root = node[root].maps[id]; } return val; } int main() { #ifdef LOCAL freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif num[18] = 1; for (int i = 17;i >= 0;i--) { num[i] = num[i + 1] * 10; //预处理出1 ~ 1e18 } id = 0; //控制字典树结点数s min_val = inf; max_val = 0; scanf("%d", &n); for (int i = 1;i <= n;i++) { scanf("%s", str2); int len = strlen(str2); strcpy(str, lead[19 - len]); strcat(str, str2); //将数补充为19位数 if (i != 1) //找最值的前提:至少有两个以上的数 { max_val = max(max_val, dfs(1)); min_val = min(min_val, dfs(0)); } insert(); } if (min_val == inf) min_val = 0; printf("%llu %llu\n", min_val, max_val); return 0; } /* 附上一组样例,如果 wrong on test 3 的话,可以用一下,这个是数极限大的情况 input 7 1000000000000000000 1000000000000000000 999999999999999999 1000000000000000000 1000000000000000000 1000000000000000000 1234567890987654321 output 1123456789876543210 2234567890987654321 */