Acwing 第 157 场周赛(8月3日)

四舍五入

给定一个实数,保证该数为正数,其小数点后至少包含一位非零的数字,且小数点后不含多余末尾 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;
}
  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值