[USACO24JAN] Cannonball B 题解

目录

题目传送门

题意与任务和结果

75分的代码

75分代码分析

修改后程序的模拟部分代码

AC代码


题目传送门

题意与任务和结果

看完题目后,我们了解到了其实题目就是一个模拟的题目,无疑就是模拟Bessie在数轴上的行动,模拟的答案是Bessie在数轴上击破的炮击目标,只是每个数轴上的整数点上都有一个需要处理的子任务。根据题意,子任务大致有两种:

  •   是这个点上的是一个炮击目标(也就是q=1)
  •   是这个点上的是一个跳板(也就是q=0)

在细分该子任务的其他子任务,那么每一个点上都有一个结果:

  •  这个点上是炮击目标,且未被炮击过且k(能量)$\ge$v时,炮击目标+1
  •  这个点上是炮击目标,且未被炮击过且k(能量)$\le$v时,炮击目标+0
  •  这个点上是炮击目标,且被炮击过时,炮击目标+0
  •  这个点上是跳板时,跳转方向翻转(如向右变向左),k值发生变化,k=k+v

 
还需要考虑什么时候结束:

  •  弹跳无限长的时间或直到她离开数轴时结束

75分的代码

那么这些我们都列举出来了,模拟这个过程也就轻而易举了。
模拟代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,s,p,q[100005],v[100005],ans,k=1;
bool flag,m[100005];
int main()
{
	cin>>n>>s;
	p=s;
	for(int i=1;i<=n;i++)
	{
		cin>>q[i]>>v[i];
	}
	if(q[p]==1)
	{
		if(k>=v[p]&&m[p]==0)
		{
			ans++;
			m[p]=1;
		}
	}
	else
	{
		k+=v[p];
		flag=1;
	}
	while(p>=1&&p<=n)
	{
		if(flag==0)
		{
			p+=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		else
		{
			p-=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		//cout<<p<<endl;
	}
	cout<<ans;
	return 0;
}

75分代码分析

我们用两个整形数组分别储存v和q,然后再用一个bool的数组储存该炮击目标是否被炮击过,再用一个bool的变量判断向左还是向右(0向右,1向左),那么程序的模拟范围就在[1,n]中,再根据上述我们列出的的子任务和结果进行模拟,那么结果就非常轻松地出来了。

但......

 上述代码结果如下

只有75分......

我们发现T了5个点。

该怎么办呢?我们不妨猜一下当Bessie在数轴上的两个跳板之间来回跳跃,那是不是就死循环了。题目中有一句话:

  • 如果 Bessie 弹跳无限长的时间或直到她离开数轴,她会击破多少个炮击目标?

也就是说但Bessie 弹跳无限长的时间或直到她离开数轴时,这个模拟程序就结束。我们好像并没有考虑到要判断是否弹跳无限长时间。所以T了。

那我们只需要加一个变量t,放在while的开头,每次都+1,如果Bessie跳到了炮击目标上,就将t给清零,因为并没有在反复跳,当t$\ge$n时,也就意味着我已经弹跳了超过n次且没有到过炮击目标,也就是跳跃时间无限长的情况,我们满足这个情况跳出这个模拟程序即可。

修改后程序的模拟部分代码

那么模拟程序也就会变成下面这个程序:

while(p>=1&&p<=n)
	{
		r++;
		if(r==n) 
		{
			break;
		}
		if(flag==0)
		{
			p+=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
					r=0;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		else
		{
			p-=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
					r=0;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		//cout<<p<<endl;
	}

这样这个问题就结束了。

AC代码

附上AC代码

#include<bits/stdc++.h>
using namespace std;
long long n,s,p,q[100005],v[100005],ans,k=1,r;
bool flag,m[100005];
int main()
{
	cin>>n>>s;
	p=s;
	for(int i=1;i<=n;i++)
	{
		cin>>q[i]>>v[i];
	}
	if(q[p]==1)
	{
		if(k>=v[p]&&m[p]==0)
		{
			ans++;
			m[p]=1;
		}
	}
	else
	{
		k+=v[p];
		flag=1;
	}
	while(p>=1&&p<=n)
	{
		r++;
		if(r==n) 
		{
			break;
		}
		if(flag==0)
		{
			p+=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
					r=0;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		else
		{
			p-=k;
			if(q[p]==1)
			{
				if(k>=v[p]&&m[p]==0)
				{
					ans++;
					m[p]=1;
					r=0;
				}
			}
			else
			{
				k+=v[p];
				if(flag==1)
				{
					flag=0;
				}
				else
				{
					flag=1;
				}
			}
		}
		//cout<<p<<endl;
	}
	cout<<ans;
	return 0;
}

完结,感谢阅读。

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值