zoukankan      html  css  js  c++  java
  • Codeforces 883D. Packmen Strike Back(二分+DP)

    传送门


    题意:一条长为n线上有几个吃豆人和几个豆子,你可以控制吃豆人的移动方向,选定方向之后吃豆人便会一直向这个方向走,问能吃到的最大豆子数和为了达到这个目标所需的最少时间。


    思路:最大的豆子数量其实就是所有的豆子,唯一有个特例就是只有一个人,有两个人以上的话,不难想出只要两人面对面走就能吃到所有豆子了,所以先处理只有一个人的情况。

    void straight()
    {
        int sta=pos[0];
        int sumlef=0,timlef=0,k=0,sumrig=0,timrig=0;
        for(int i=sta-1;i>=1;i--)
        {
            k++;
            if(s[i]=='*')
            {
                sumlef++;
                timlef=k;
            }
        }
        k=0;
        for(int i=sta+1;i<=n;i++)
        {
            k++;
            if(s[i]=='*')
            {
                sumrig++;
                timrig=k;
            }
        }
        if(sumlef<sumrig || (sumlef==sumrig && timrig<timlef))
        cout<<sumrig<<" "<<timrig<<endl;
        else
        cout<<sumlef<<" "<<timlef<<endl;
    }
    

    然后就是怎么求最小时间了,数据范围是1e6,所以应该是O(nlogn)或O(n)的算法,再加上这道题明显符合单调性(X秒内能吃到Y颗豆子,那么X+1秒内一定可以吃到Y颗豆子)所以我们可以二分时间。

    void binary()
    {
        int l=0,r=n,mid,ans=0;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid))
            {
                r=mid-1;
                ans=mid;
            }
            else
            l=mid+1;
        }
        cout<<sum[n]<<" "<<ans<<endl;
    }
    

    然后就是如何check的问题,我们可以用DP来解决,dp[i]表示前i个人能吃到的从左数最右边的豆子(就是前i个人能吃到的最右边的豆子),dp[i]可以从dp[i-1]再加上第i个人吃的豆子转移得到,但一共有三种情况,取最大值。
    一、第i个人向左走。
    二、第i个人向右走。
    三、第i-1个人向右,第i个人向左。
    分别算就行了,当然,为了求第i个人到第i-1个人之见豆子的数量,要提前预处理好前缀和。

    void prework()
    {
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='*')
            sum[i]=sum[i-1]+1;
            else
            sum[i]=sum[i-1];
            if(s[i]=='P')
            pos.pb(i);
        }
        m=pos.size();
    }
    inline bool nothing(int l,int r)
    {
        return (r<l || !(sum[r]-sum[l-1]));
    }
    bool check(int tim)
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<m;i++)
        {
            if(nothing(dp[i]+1,pos[i]-tim-1))
            dp[i+1]=max(dp[i+1],pos[i]);
    
            if(nothing(dp[i]+1,pos[i]-1))
            dp[i+1]=max(dp[i+1],pos[i]+tim);
    
            if(i>=1 && nothing(dp[i-1]+1,pos[i]-tim-1) && pos[i]-tim<pos[i-1])
            dp[i+1]=max(dp[i+1],pos[i-1]+tim);
        }
        if(nothing(dp[m]+1,n))
        return 1;
        return 0;
    }
    

    最后是完整的AC代码

    //头文件日常省略
    using namespace std;
    const int maxn=1000005;
    string s;
    int n,m;
    vector<int> pos;
    int sum[maxn],dp[maxn];
    void prework()
    {
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='*')
            sum[i]=sum[i-1]+1;
            else
            sum[i]=sum[i-1];
            if(s[i]=='P')
            pos.pb(i);
        }
        m=pos.size();
    }
    void straight()
    {
        int sta=pos[0];
        int sumlef=0,timlef=0,k=0,sumrig=0,timrig=0;
        for(int i=sta-1;i>=1;i--)
        {
            k++;
            if(s[i]=='*')
            {
                sumlef++;
                timlef=k;
            }
        }
        k=0;
        for(int i=sta+1;i<=n;i++)
        {
            k++;
            if(s[i]=='*')
            {
                sumrig++;
                timrig=k;
            }
        }
        if(sumlef<sumrig || (sumlef==sumrig && timrig<timlef))
        cout<<sumrig<<" "<<timrig<<endl;
        else
        cout<<sumlef<<" "<<timlef<<endl;
    }
    inline bool nothing(int l,int r)
    {
        return (r<l || !(sum[r]-sum[l-1]));
    }
    bool check(int tim)
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<m;i++)
        {
            if(nothing(dp[i]+1,pos[i]-tim-1))
            dp[i+1]=max(dp[i+1],pos[i]);
    
            if(nothing(dp[i]+1,pos[i]-1))
            dp[i+1]=max(dp[i+1],pos[i]+tim);
    
            if(i>=1 && nothing(dp[i-1]+1,pos[i]-tim-1) && pos[i]-tim<pos[i-1])
            dp[i+1]=max(dp[i+1],pos[i-1]+tim);
        }
        if(nothing(dp[m]+1,n))
        return 1;
        return 0;
    }
    void binary()
    {
        int l=0,r=n,mid,ans=0;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid))
            {
                r=mid-1;
                ans=mid;
            }
            else
            l=mid+1;
        }
        cout<<sum[n]<<" "<<ans<<endl;
    }
    int main()
    {
        cin>>n>>s;
        s=" "+s;
        prework();
        if(m==1)
        straight();
        else
        binary();
        return 0;
    }
    
  • 相关阅读:
    Educational Codeforces Round 6
    Educational Codeforces Round 5
    [Educational Round 5][Codeforces 616F. Expensive Strings]
    [Codeforces Round #508 (Div. 2)][Codeforces 1038E. Maximum Matching]
    Codeforces Round #509 (Div. 2)
    从零开始编写一个vue插件
    Web前端错题模糊题记录
    vue中响应式props办法
    vue中router-link的click事件失效的解决办法
    ubuntu修改顶栏颜色
  • 原文地址:https://www.cnblogs.com/NightRaven/p/9459741.html
Copyright © 2011-2022 走看看