E. Packmen(贪心+二分[注意细节])

这题虽然想到了贪心+二分,但是模拟细节wa到怀疑人生…

因 为 考 虑 每 个 p 有 两 种 走 法 , 向 左 走 再 向 右 走 因为考虑每个p有两种走法,向左走再向右走 p,

或 者 向 右 走 再 向 左 走 或者向右走再向左走

那 么 我 们 先 考 虑 第 一 个 p 那么我们先考虑第一个p p

这 个 p 左 边 的 所 有 ∗ 都 被 吃 掉 , 设 走 到 最 左 边 的 ∗ 距 离 是 l 这个p左边的所有*都被吃掉,设走到最左边的*距离是l p,l

然 后 右 边 也 会 有 部 分 ∗ 被 吃 掉 , 设 走 到 最 右 边 的 ∗ 是 r 然后右边也会有部分*被吃掉,设走到最右边的*是r ,r

所 以 就 是 需 要 走 m i n ( l , r ) ∗ 2 + m a x ( l , r ) 所以就是需要走min(l,r)*2+max(l,r) min(l,r)2+max(l,r)

然 后 记 录 一 下 这 个 p 走 到 最 右 边 的 点 r 然后记录一下这个p走到最右边的点r pr

下 一 个 p 需 要 把 r 到 自 己 间 的 ∗ 都 吃 掉 下一个p需要把r到自己间的*都吃掉 pr

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define f first
#define s second
typedef pair<int,int>p;
int n,top,Left,in[maxn],sumn;
char a[maxn];
bool isok(int mid)
{
	int l=Left,temp=0;//l表示当前没有被吃掉的最左边的* 
	for(int i=1;i<=top;i++)
	{
		int index=in[i],q=in[i];//当前p的位置是index 
		for(int j=index-1;j>=l;j--)//首先把[l,index-1]所有*吃掉 
		{
			if(a[j]!='*')	continue;
			if(index-j>mid)	return false;
			q=j,temp++;//消掉星星	
		}
		l=index+1;
		for(int j=index+1;j<=n;j++)//现在就尽可能多吃[index+1,n]的* 
		{
			if(a[j]=='P')	break;//留给下一个p去处理吧,下一个p吃的更远 
			if(a[j]=='.')	continue;
			int now=j-index;//假设这个now是吃掉的最右边的点 
			if(min(now,index-q)*2+max(now,index-q)<=mid)
				temp++,l=j+1;
			else	break;	
		} 
	}
	if(temp==sumn)	return true;
	else	return false;
}
int main()
{
	cin >> n >> (a+1);
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='*')
		{
			sumn++;
			if(Left==0)	Left=i;
		}
		else if(a[i]=='P')	in[++top]=i;
	}
	int l=1,r=n*2,mid,ans;
	while(r>=l)
	{
		mid =l+r>>1;
		if( isok(mid) )	r=mid-1,ans=mid;	
		else	l=mid+1;
	}
	cout<<ans;
}

另一份贪心+二分

思 路 来 源 于 思路来源于 点我呀

#include <bits/stdc++.h>
using namespace std;
int n;
char s[200009];
bool isok(int mid)
{
	int pos=-1,last=-1;//pos记录*的位置,last是当前覆盖到的最远点 
	for(int i=1;i<=n;i++)
	{
		if(i>last&&s[i]=='*'&&pos==-1)	pos=i;//遇到没有标记的*
		if(s[i]=='P')
		{
			if(pos==-1)//前面没有*,直接往右走 
				last=i+mid;	
			else
			{
				int x = i-pos;//求出去左边的距离
				if( x>mid )	return false;//到不了
				if( x*3<mid )//先左边再右边可以往右走得更远
					last=i+mid-2*x;
				else	//先右边再左边
					 last=(mid-x)/2+i;
			}
			pos=-1;
		}	
	} 
	if(pos==-1)	return true;
	return false;
}
int main()
{
	cin >> n >> (s+1);
	int l=1,r=n*2,ans=-1;
	while(r>=l)
	{
		int mid = (l+r)/2;
		if( isok(mid) )	r=mid-1,ans=mid;
		else	l=mid+1;
	}
	cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值