CF847E Packmen(二分答案+贪心)

题目链接

 

该题在DP的题单里,但却没有想出用DP的解法。题解里也基本都是二分的解法。

但是大部分的题解的特判特别多,这边用了思路比较简单的贪心方法。贪心方式绝对没有问题,虽然本人没有证明出来,但理解后就会发现思路比较简单,代码短,且一定正确。

1.首先对每个食物'*'找到它后面最近的人'P'所在的位置,若没有的话那么位置记为0.存在post数组中。

2.二分答案,记为mid。主要是check如何判断。

3.从前向后遍历,会有两种情况:

1->如果先遇到食物'*',那么说明post[i]一定要吃掉该食物(因为前面没有P),

那么此处就要算一下能跑的最大距离:

(1)P先跑到i再尽力向后跑,那么往后跑的最远距离为post[i]+mid-(post[i]-i)*2

(2)P先向后跑一段,再跑到i位置,当然不用再跑到i前面的位置了,因为没有食物'*'在i的前面了。那么能尽力向后跑的那一段为post[i]+(mid-post[i]+i)/2

上面那两个值取最大记在r中,表示1~r里面的食物都已经被当前这个P吃掉了。并把这个P标记已经用过。

然后继续往后跑,对于1~r段中的‘*’都不用再去管了,只用管r+1~n中的食物‘*’即可。但1~r段中剩下没被标记过的P要尽力往后跑mid段距离(不用考虑往前跑了,因为前面的食物已经没了),并更新r=max(r,i+mid),表示1~r段的食物都吃掉了

也就是说只用对r+1~n这段继续上述的操作即可。

2->如果先遇到P,那么直接更新r=max(r,i+mid),表示1~r的食物已经吃完了。

然后对r+1~n段继续上述操作

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+50;
int n;
char s[maxn];
int post[maxn];
bool st[maxn];
void init(){
    for(int i=n,pos=0;i>=1;i--){
        if(s[i]=='P') pos=i;
        if(s[i]=='*') post[i]=pos;
    }
}
bool check(int mid){
    for(int i=1;i<=n;i++) st[i]=false;
    for(int i=1,r=-1;i<=n;i++){//pos表示第一个*的位置
        if(s[i]=='P'&&!st[i]) r=max(r,i+mid);
        if(s[i]=='*'&&i>r){
            if(!post[i]||post[i]-i>mid) return false;
            int d1=post[i]+mid-(post[i]-i)*2,d2=post[i]+(mid-post[i]+i)/2;
            st[post[i]]=true;
            r=max(r,max(d1,d2));
        }
    }
    return true;
}
void solve(){
    cin>>n>>s+1;init();
    int l=1,r=maxn*2,ans;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)){
            r=mid-1,ans=mid;
        }
        else l=mid+1;
    }
    cout<<ans<<endl;
}
int main(){
    int t;t=1;
    while(t--) solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值