1 条题解
-
0
思路
记输入中加号个数为 ,则最终要把所有数字字符分成 个正整数相加。
最优性分析
- 长度必须尽量均匀。 设某个数的长度比另一个数至少多 。把较长数最高位的数字挪到较短数的最高位:它占的位权从 变成 ,其余位权不变。因为 ,有 ,而所有数字都是正数,总和严格减小。所以最优解中任意两个数的长度差不超过 。
- 长度固定后是重排不等式。 总和 ,对每个数字是线性的。长度分配固定后,所有位权 的集合固定;由重排不等式,把最小的数字配最大的位权(等价于每个数内部升序、且整体按位权从高到低依次放最小的数字)总和最小。
- 轮询正是这个配对。 把数字升序排序,第 轮把第 批 个数字分别填到 个数当前未填的最高位,正好实现了"位权降序配数字升序",同时保证各数长度差不超过 。
贪心构造
- 将所有数字字符按升序排序(加号字符 ASCII 比数字小,排序后自然排在最前面,直接跳过即可);
- 按轮询(round-robin)的方式分配:第
cnt个数字放进第cnt mod p个数,等价于第 轮把第 批 个数字依次填到 个数当前未填的最高位; - 最后把这 个数按位相加得到答案(用结构体实现大数加法,注意处理进位)。
复杂度:排序 ,分配与逐位相加 ,空间 。
代码
#include<bits/stdc++.h> #define int long long #define str string #define all(x) x.begin(),x.end() #define pb push_back using namespace std; // 大整数结构体:a 按高位到低位存每一位数字 struct node{ vector<int>a; int len; node(): a(), len(0) {} // 在末尾追加一位数字 void add(int num){ a.pb(num); len++; } // 重载加法运算符,实现大数相加 node operator+(const node&x)const{ auto a1 = a, a2 = x.a; // 拷贝一份,避免修改原对象 reverse(all(a1)); // 翻转成低位在前,方便从个位开始加 reverse(all(a2)); node res; int plus = 0; // 进位 int mx = max(len, x.len); for(int i=0;i<mx;i++){ int n1 = i<len? a1[i]:0; // 超出长度的位补 0 int n2 = i<x.len? a2[i]:0; int t = (n1+n2+plus) % 10; // 本位结果 plus = (n1+n2+plus) / 10; // 向高位的进位 res.a.pb(t); } if(plus){res.a.pb(plus);} // 最高位还有进位就再补一位 res.len = res.a.size(); reverse(all(res.a)); // 翻回高位在前 return res; } // 打印大数 void pr(){ for(int i=0;i<len;i++){ cout<<a[i]; } cout<<endl; } }; void go(){ str s;cin>>s; int cn = count(all(s), '+'); // 加号个数,结果要分成 cn+1 个数 vector<node>a(cn+1); int p = cn+1; int cnt = 0; sort(all(s)); // 数字升序排列 for(char c:s){ if(c=='+')continue; // 跳过加号 int dec = c-'0'; a[cnt%p].add(dec); // 轮询分配:第 cnt 个数字交给第 cnt%p 个数 cnt++; } // 把 k+1 个数全部相加 node ans; for(int i=0;i<=cn;i++){ ans = ans+a[i]; } ans.pr(); } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int _=1; // 本题只有一组数据,不需要读入测试组数 while(_--){ go(); } return 0; }
- 1
信息
- ID
- 19
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者