P7078 [CSP-S2020] 贪吃蛇AC题解

本人 6年级 耗时2个月luoguP7078 [CSP-S2020] 贪吃蛇AC

接下来由我为大家讲解

首先有一个结论:

当前最强的蛇吃了最弱的蛇之后,如果没有变成最弱的蛇,他一定会选择吃!

证明:

假设当前最强的蛇叫石老板。

  • 如果下一条最强的蛇如果依旧是石老板,那肯定不吃白不吃;

  • 如果下一条最强蛇不是石老板,此时最强的蛇没有原先强,最弱的蛇也没原先弱,吃掉后肯定比石老板要弱。也就是说,当前最强的蛇吃了之后,如果会死,也会死在石老板前面。那么这样一来,这条蛇会想尽办法不死,从而石老板也一定能不死。

有了这个结论,一部分蛇可以放心大胆地吃了,但是问题来了,如果吃了之后变成最弱的蛇了,到底选择吃不吃呢?

稍微往后推一推就明白了:

当前最强蛇记为石老板,下一条最强蛇记为喵老板。石老板进食后变成最弱的蛇了,如果喵老板进食后不是最弱的蛇,他就会选择吃(根据开头的结论),这样石老板就凉了,所以石老板当初的选择一定是不吃。

如果喵老板进食后依旧是最弱的蛇,那就会考虑下一条最强蛇的情况,起名为汪老板。同样分两种情况:如果汪老板进食后不是最弱的蛇,那他就会选择吃,这样喵老板就凉了,所以他当初会选择不吃,这样石老板就不会死,那么石老板当初就会选择吃。如果汪老板进食后变成了最弱的蛇,那就再考虑下一条蛇………………

这个问题就变成了一个递归的问题了,直到某条蛇吃了之后不是最弱的蛇或者只能下两条蛇为止。这样,最后一条蛇会选择吃,倒数第二条蛇为了保命会选择不吃,倒数第三条蛇可以放心大胆的吃,倒数第四条蛇会保命选择不吃,倒数第五条蛇可以放心吃………………

这样,石老板选择吃不吃,就和最后一条蛇之间的奇偶性相关了。并且石老板选择不吃,游戏结束,石老板选择吃,游戏也会在下一轮结束(因为喵老板会选择不吃)。

到目前为止,这个题目很清晰了,只需模拟两个阶段即可:

阶段一:所有最强蛇进食后都不是最弱蛇,放心大胆吃!

阶段二:所有最强蛇进食后都是最弱蛇,直到有条蛇可以放心吃为止(吃了后不是最弱或者只剩两条)

阶段一结束时,游戏就基本结束了(根据阶段二的奇偶性看能不能再吃一次)

接下来送出AC代码 代码为C++14 运行

感谢大家的收看

#include<bits/stdc++.h>
#define inl inline
using namespace std;
namespace FGF
{
	int n,m;
	const int N=1e6+5;
	int b[N],a[N],q[N],de[N],h,t,st,ed,fl,end;
	struct Node{
		int mx,mi;
		Node(){};
		Node(int _mx,int _mi):mx(_mx),mi(_mi){};
	}c[N];
	int read()
	{
		int s=0;char ch=getchar();
		while(!isdigit(ch))ch=getchar();
		while(isdigit(ch))s=s*10+ch-'0',ch=getchar();
		return s;
	}
	inl int getcm() 
	{
		int mi;
		if(st>ed)mi=q[t];
		else if(h>t)mi=st;
		else if(a[st]<a[q[t]]||(a[st]==a[q[t]]&&st<q[t]))mi=st;
		else mi=q[t];
		return mi;
	}
	inl int getmi()
	{
		int mi;
		if(st>ed)mi=q[t],t--;
		else if(h>t)mi=st,st++;
		else if(a[st]<a[q[t]]||(a[st]==a[q[t]]&&st<q[t]))mi=st,st++;
		else mi=q[t],t--;
		return mi;
	}
	inl int getmx() 
	{
		int mx;
		if(st>ed)mx=q[h],h++;
		else if(h>t)mx=ed,ed--;
		else if(a[ed]>a[q[h]]||(a[ed]==a[q[h]]&&ed>q[h]))mx=ed,ed--;
		else mx=q[h],h++;
		return mx;
	}
	void solve()
	{ 
		h=1,t=0,st=1,ed=n,fl=0,end=n-1;
		for(int i=1;i<n;i++)
		{
			int mx=getmx(),mi=getmi(),cm=getcm();
			a[mx]-=a[mi];
			q[++t]=mx;
			c[i].mx=mx,c[i].mi=mi;
			if(a[mx]>a[cm]||(a[mx]==a[cm]&&mx>cm))
			{
				if(fl)
				{
					end=i;
					break;
				}
			}
			else fl=i;
		}
		for(int i=1;i<=n;i++)de[i]=end+1;
		de[c[end].mi]=end;
		int ans=end+1;
		for(int i=end-1;i;i--)
		{
			if(de[c[i].mx]<ans)ans=i;
			de[c[i].mi]=i;
		}
		printf("%d\n",n-ans+1);
	}
	void work()
	{
		int T=read();n=read();
		for(int i=1;i<=n;i++)
			b[i]=a[i]=read();
		solve();
		for(int i=2;i<=T;i++)
		{
			int k=read();
			for(int j=1;j<=n;j++)a[j]=b[j];
			for(int j=1;j<=k;j++)
			{
				int x=read(),y=read();
				b[x]=a[x]=y;
			}
			solve();
		}
	}
}
int main()
{
	FGF::work();
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值