1 条题解

  • 0
    @ 2026-4-30 20:57:36

    思路:

    我们的二分是找答案的(俗称二分答案……),而我认为这道题真正的难点在于验证这个答案是否可行,所以说:DP 是个好东西,我们先将所有的活动点进行排序,进行预处理后,使用我们提前声明好的数组:dpdpdpi,jdp_{i,j} 表示为覆盖前 ii 个点,用了 jj 台大型机,最少需要的小型机台数):

    • 初始 dp0,0=0dp_{0,0}=0
    • 从当前第一个未覆盖的 ii 开始:
      • 放小型机:跳到小型机能覆盖的点 +1+1 处,小型机数量 +1+1
      • 放大型机:跳到大型机能覆盖的点 +1+1 处,大型机数量 +1+1
    • 取最小值。

    最后,我们只需要判断是否存在 jj 使得 dplen,jPdp_{len,j} \le Plenlen 代表长度),有则这个 ww 成立,无则这个 ww 不成立。

    主函数部分十分简单,输入完后开始二分求答案,每次二分得出一个 ww,判断这个 ww 成不成立,如果成立的话,往下二分,否则往上二分,二分证明:

    因为:若 ww 可行,则 w+1w+1 一定可行,但 w1w-1 不一定可行。

    所以:我们这里要求的答案 ww 就是满足 w1w-1 不可行的,也就是我们开头提到的最小的最大。

    AC 代码:

    #include<bits/stdc++.h> //万能头文件
    using namespace std;
    const int INF=1e9;
    int n,m,q;
    vector<int> p;
    
    bool check(int w)  //判断w是否可行
    {
        int len=p.size();
        vector<int> ns(len),nl(len);
        for(int i=0;i<len;i++)
        {
            ns[i]=upper_bound(p.begin(),p.end(),p[i]+w-1)-p.begin()-1;
            nl[i]=upper_bound(p.begin(),p.end(),p[i]+2*w-1)-p.begin()-1;  //预处理
        }
        int maxq=min(q,n);
        vector<vector<int>> dp(len+1,vector<int>(maxq+1,INF));  //声明dp数组
        dp[0][0]=0;
        for(int i=0;i<len;i++)
        {
            for(int j=0;j<=maxq;j++)
            {
                if(dp[i][j]==INF)
                {
                    continue;
                }
                dp[ns[i]+1][j]=min(dp[ns[i]+1][j],dp[i][j]+1);  //求用大机子和小机子的最小值
                if(j<maxq)
                {
                    dp[nl[i]+1][j+1]=min(dp[nl[i]+1][j+1],dp[i][j]);
                }
            }
        }
        for(int j=0;j<=maxq;j++)
        {
            if(dp[len][j]<=m)  //如果存在
            {
                return true;
            }
        }
        return false; //不存在
    }
    
    int main()
    {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>n>>m>>q;
        p.resize(n);
        for(int i=0;i<n;i++)
        {
            cin>>p[i];
        } //简单输入
        sort(p.begin(),p.end());  //排序
        int l=1,r=p.back()-p.front()+1,ans=r;
        while(l<=r)  //二分答案
        {
            int mid=(l+r)/2;
            if(check(mid))  //若这个w可行
            {
                ans=mid;
                r=mid-1;  //往下二分求最小值
            }
            else
            {
                l=mid+1;  //往上二分求可以满足条件的w的值
            }
        }
        cout<<ans; //输出答案
        return 0;  //好习惯从今天养成
    }
    

    看到这了,点个赞吧!(QAQ)……

    • 1

    信息

    ID
    10146
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者