Codeforces Round #802 (Div. 2)

这篇博客介绍了三个算法问题的解决方案:A. OptimalPath 是寻找图中从(1,1)到(n,m)的最短路径,通过边界行走策略实现;B-PalindromicNumbers 则是关于如何构造回文数,通过分类讨论和高精度减法完成;C. HelpingtheNature 中,通过分析操作对差分数组的影响,找到使所有数变为0的最少操作次数。这些问题展示了算法在解决数学和计算机科学问题中的应用。
摘要由CSDN通过智能技术生成

A.Optimal Path

题意:

给一个n*m的图,从(1,1)走到(n,m)的花费最小值

花费的定义是 w(i,j)=(i-1)*m+j

思路:

很简单的思路 沿着边界走就是最小值了

#include <bits/stdc++.h>
using namespace std;
void solve()
{
    int n,m;
    cin>>n>>m;
    long long res=0;
    for(int i=1;i<=m;i++)
        res+=i;
    for(int i=2;i<=n;i++)
        res+=(i*m);
    cout<<res<<endl;
    return ;
}
int main()
{
    int t;

    cin>>t;
    while(t--)
        solve();


    return 0;
}

B - Palindromic Numbers

题意

给一个字符串,然后去求一个和给定字符串的长度相同的串,使得原来串加这个串后的结果是一个回文数 已知一定有解

思路:

可以去分类讨论,如果给定字符串第一位不是9,那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 9999 这样的数字

证明 以四位数为例子 最大的并且第一位不是 9  的数是 8999 如果要让他变成 9999 那么就需要 1000才能变成 9999,而1000是符合条件的 那么小于8999的四位数所需要变成 9999 的数肯定也是4位的 并且比 1000 要大的 那么就可以证明 如果给定字符串第一位不是9,那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 9999 这样的数字

第二种情况

如果第一位是9 那么一定存在一个和 给定字符串长度相同的 串使得两者相加后的结果是 11111.... 这样的数字

比如 9999 变成 11111 就需要 1112 证明和上面差不多

然后因为这个数很大 所以我们写一个高精度减法就可以过了

#include <bits/stdc++.h>
using namespace std;
vector<int> sub(vector<int> &a,vector<int> &b)
{
    vector<int> c;
    for(int i=0,t=0;i<a.size()||i<b.size();i++)
    {
        t=a[i]-t;
        if(i<b.size()) t-=b[i];
        c.push_back((t+10)%10);
        if(t<0) t=1;
        else t=0;
    }
    while(c.size()>1&&c.back()==0) c.pop_back();


    return c;
}
void solve()
{
    string s;
    int n;
    cin>>n;
    cin>>s;
    vector<int> a;
    bool st=false;
    for(int i=s.size()-1;i>=0;i--)
    {
        a.push_back(s[i]-'0');
        if(i==0)
        {
            if(s[i]-'0'==9)
                st=true;
        }
    }
    vector<int> b;
    if(st)
    {
        for(int i=0;i<=n;i++)
            b.push_back(1);
    }
    else
    {
        for(int i=0;i<n;i++)
            b.push_back(9);
    }
    auto c=sub(b,a);
    for(int i=c.size()-1;i>=0;i--)
        cout<<c[i];
    cout<<endl;
    return ;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

C.Helping the Nature

题意

给出一个序列a,可以做3种操作

  1. 选择一个i,给a[1∼i]减一
  2. 选择一个i,给a[i∼n]减一
  3. 给所有数加一 问最少多少次操作才能使所有数都变为0

思路:

把所有数都变成0就等价于它的差分数组全部变成0,那么这三个操作就等价于

差分数组为d [i] 

  1. 减一,di+1加一
  2. di减一
  3. d1加一

 可以看得出来 除了1以外的位置 只有第一个操作可以加1 除了1以外的位置 只有第二个操作可以减1 那么对于位置1以外的位置变成0的最小操作数其实是固定的,那么位置1的最小操作数也是固定的了 ( 操作完2-n位置的数然后把d[1]直接变成0就是最小操作数的总数 )

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
long long f[N];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        f[i]=a[i]-a[i-1];
    long long res=0;
    for(int i=2;i<=n;i++)
    {
        if(f[i]>0)
            res+=f[i];
        else if(f[i]<0)
        {
            res-=f[i];
            f[1]+=f[i];
        }
    }
    cout<<res+abs(f[1])<<endl;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

 D.River Locks

题意:

给出一些连续的水库,每个水库都有一个管道可以注水,速率为每秒一个单位,每个水库都有个容量vi,水位超过容量时水就会流入到其下游的其他水库,第n个水库的水溢出后会流入河里.

初始状态每个水库里都没有水,每次询问给出一个时间t,问至少要打开多少个管道才能在t时间内给n个水库都填满水.

思路:

可以想到最多就只能开n个管道 如果全开了都不能在t秒内解决问题 那就肯定不行 并且如果只开了x个管道就可以解决问题 那开x+1 ~ n 个管道肯定也可以解决问题 那么这种思路是不是就很符合二分呢?对的,就可以使用二分了

首先考虑不合法的情况

 可以使用 此类推 i=1~n 充满所需要的最短时间为\sum_{k=1}^{i}v(k)/i,取最大值判断有无解

我们能注意到,我们应该尽量打开上游的水库,这样才能尽量防止水浪费流到河里.如此开x个水龙头填满所有水库需要的时间就是⌈\sum v/x⌉,二分一下答案即可.

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
int a[N],ans[N];
long long s[N];
int main()
{
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    long long maxv=0;
    for(int i=1;i<=n;i++)
    {
        s[i]=s[i-1]+a[i];
        maxv=max(maxv,(s[i]+i-1)/i);
    }
    cin>>m;
    while(m--)
    {
        int x;
        cin>>x;
        if(x<maxv)
        {
            cout<<-1<<endl;
            continue;
        }
        int l=1,r=n;
        while(l<r)
        {
            int mid=l+r>>1;
            if(1LL*x*mid>=s[n]) r=mid;
            else l=mid+1;
        }
        cout<<r<<endl;

    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值