四舍五入
给定一个实数,保证该数为正数,其小数点后至少包含一位非零的数字,且小数点后不含多余末尾 0
如果将该实数转化为字符串,则字符串的长度为 n
现在,你可以对该实数进行不超过 t 次(也可以零次)四舍五入操作。
每次操作可以将当前数四舍五入到小数点后的任意位置(也可以四舍五入到最接近的整数)。
例如,1.645
四舍五入到小数点后两位可以得到 1.65,1.141四舍五入到小数点后一位可以得到 1.1
,3.762四舍五入到最接近的整数可以得到 4
我们希望通过四舍五入操作,将给定实数变得尽可能大。
请你输出通过上述操作可以得到的最大可能值。
输入格式
第一行包含两个整数 n,t
第二行包含给定正实数,保证其既包含整数部分也包含小数部分,且小数点后至少包含一位非零的数字,且小数点后不含多余末尾 0
如果将该实数转化为字符串,则字符串的长度为 n
输出格式
输出一个实数,表示可以得到的最大可能值。
输出答案不应包含小数点后多余末尾 0
如果答案是整数,则直接输出整数。
数据范围
前 4个测试点满足 1≤n≤12,1≤t≤100
所有测试点满足 1≤n≤2×105,1≤t≤109
输入样例1:
6 1
10.245
输出样例1:
10.25
输入样例2:
6 2
10.245
输出样例2:
10.3
输入样例3:
3 100
9.2
输出样例3:
9.2
分析问题:
- num1 < num2 可以推出 f[num1] <= f[num2] 吗?(f[x]表示x四舍五入任意次的最优解)
可以推出来,假设不能推出,那num1 < num2 ,f[num1] > f[num2]的情况只会发生在两个数非常接近的情况(即中左往右看,两数各位置的数字都相等,直到有一位数字相差1),这种时候,num1要尽可能大,假设在这一位num1能进位,那num1最好的结果就是和num2相等,f[num2]最小的可能也只能是等于num2,因此,num1 < num2 => f[num1] <= f[num2]
思路:
既然 num1 < num2 => f[num1] < f[num2] ,那每次对题目给定的数求最优就是答案。
英语: 表示“整” entire、full、complete。表示“部分” part、some、chunk、seation、partion、tranche。
- 这段代码会超时
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int n, t;
double y;
double getAns(double x) {
double small = x;
double a = 0;
while(small > 1e-7) {
int t = (int)(small * 10);
if (t > 4) {
a += 0.1;
break;
}
a = (a + t) * 0.1;
small = small * 10 - t;
}
//cout << a <<" ";
double ans = 0;
while(a > 1e-7) {
int t = (int)(10 * a);
//cout << "a=" << a << "t=" << t << (a * 10 == t)<<endl;
ans = (ans + t) * 0.1;
a = a * 10 - t;
}
return ans;
}
int main() {
cin >> n >> t >> y;
int entire = (int)y;
double ans = y - entire;
while(t--) {
double ta = getAns(ans);
if (ta == ans) break;
ans = ta;
}
cout << ans + entire;
return 0;
}
中间出了一点小插曲
- 这段代码为什么会死循环?
关键在于浮点数x的精度以及它在每次迭代中被更新的方式。
浮点数在计算机中的表示是有误差的,特别是在进行算术运算时,这些误差会被累积。
在本例中,x的值为0.245,但由于浮点数的精度限制,实际上可能略微偏离这个值。当x乘以10时,它的值会增加,但是由于浮点数的精度,它永远不会精确地变成2.45。
因此,每次迭代中减去的t都不会完全抵消x的增加量,导致x的值在每次迭代后都会略微减小,但永远不会变为零。
#include<iostream>
using namespace std;
int main() {
double x = 0.245;
while(x) {
int t = (int)x;
x = x * 10 - t;
cout << "t=" << t << "x=" << x<<endl;
}
return 0;
}
后来决定用字符串做
- 字段代码超时了
- 问题1: 多次对字符串检查10进位操作
- 问题2:多次重新找小数点,这里可以通过维护小数点位置优化
- 问题3:每次操作以后可以从当前位置的前一个位置开始,不必再从头开始
#include<iostream>
using namespace std;
int n, t;
string s;
// 没必要将这段代码封装成函数,因为,每一位最多只会进一次位,所以,只需要执行一次除10
string dis10(string ts) {
for (int i = 0; i < n; i++) {
if (ts[i] == 1 + '9') {
ts[i] = '0';
if (i)
ts[i - 1]++;
else {
ts = '1' + ts;
i++;
n++;
}
ts = dis10(ts);
break;
}
}
return ts;
}
int getP(string s) {
int p = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '.') p = i + 1;
}
return p;
}
int main() {
cin >> n >> t >> s;
while(t--) {
int p = getP(s);
int po = 0;
for (int i = p; p && i < n; i++) {
if (s[i] > '4') {
po = 1;
if (s[i- 1] == '.') {
s[i - 2]++;
}else {
s[i - 1]++;
}
s[i] = '0';
n = i;
//cout << s << endl;
s = dis10(s);
break;
}
}
if (!po) break;
}
int p = getP(s);
for (int i= 0; i < p - 1; i++) cout << s[i];
if (p != n) cout << '.';
for (int i = p; i < n; i++) cout << s[i];
return 0;
}
封印序列
- 参考 245
- 使用并查集 联想 疯狂的馒头
-
- 没跑过,还没找到原因
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N], p[N], n;
long long v[N], an[N];
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main() {
cin >> n;
long long ans = 0;
for (int i =1; i <= n; i++) p[i] = i;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = n; i >0; i--) {
int k = b[i];
v[k] = a[k];
//cout << "ai=" << a[k];
ans = max(ans, v[k]);
if (v[k - 1]) {
int t = find(p[k - 1]);
v[k] += v[t];
p[t] = k;
ans = max(ans, v[k]);
// cout << "vt=" << v[t];
}
if (v[k + 1]) {
int t = find(p[k + 1]);
v[k] += v[t];
p[t] = k;
ans = max(ans, v[k]);
// cout << "vk=" << v[k]<<endl;
}
an[i - 1] = ans;
}
for (int i = 1; i <= n; i++) printf("%lld\n", an[i]);
// cout << 0;
return 0;
}