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;
}

猜你喜欢

转载自www.cnblogs.com/NightRaven/p/9459741.html