CF2061C Kevin and Puzzle 题解

动规题。

动态规划分为 $4$ 步:

  • 一、定义数组元素含义。

  • 二、找到数组元素之间的关系式。

  • 三、找出初始值。

  • 四、检查初始化与边界。

首先我们根据测试样例,可以发现判别一个人是否说谎的方法。

我们设当前是第 $i$ 个人。

  • 性质一:如果第 $i$ 个人说谎,那么根据题目所给“没有两个说谎的人站在一起”这一性质得到第 $i-1$ 个和第 $i+1$ 个位置的数字相差为 $1$。

  • 性质二:如果第 $i$ 个和第 $i-1$ 个人说的都是真话,那么他们位置上的数字相等。

性质找完了,我们开始考虑使用 $dp$ 求解。

第一步

设 $f_{i,0/1}$ 为当前递推到了第 $i$ 个人,且第 $i$ 个人说的是真话/谎话的方案数。

第二步

可以发现,若第 $i$ 个人说谎,那么说明他的前面的人和后面的人说的都是真话,$f_{i,1}$ 的方案数即为 $f_{i,0}$。

若第 $i$ 个人说真话,那么他的前面的人说的可能是真话也可能是谎话,我们需要分类讨论。

  • 若前面的人说的是谎话,需要满足性质一,即要求第 $i$ 和第 $i-2$ 的位置上的数相差为 $1$,若满足条件加上 $f_{i-1,1}$。

  • 若前面的人说的是谎话,需要满足性质二,即要求第 $i$ 和第 $i-1$ 的位置上的数相等,若满足条件加上 $f_{i-1,0}$。

第三步

$f_{1,0}=0/1,f_{1,1}=1$。

从第一个位置入手,可以发现第一个位置不管是什么数字一定存在可以说谎的方案,且不一定存在说真话的方案:若第一个位置的数字不为 $0$ 则说谎,反之可能说真话,如此初始化即可。

第四步

自己检查一下吧,别忘了取模。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N=2e5+10,p=998244353;
int t,n,a[N],f[N][2];

signed main(){
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			f[i][0]=0;f[i][1]=0;
		}
		f[1][1]=1;
		if(a[1]==0) f[1][0]=1;
		else f[1][0]=0;
		for(int i=2;i<=n;i++){
			f[i][1]=f[i-1][0];
			if(a[i]==a[i-1]){
				f[i][0]+=f[i-1][0];
				f[i][0]%=p;
			}
			if(a[i]==a[i-2]+1){
				f[i][0]+=f[i-1][1];
				f[i][0]%=p;
			}
		}
		int ans=(f[n][0]+f[n][1])%p;
		cout<<ans<<endl;
	}
	return 0;
}
posted @   zazic  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示