atcoder Colorful Hats 2 (递归,思维)

题目大意:

现在有一串数an,同时我们已知有三种颜色的帽子。假设为R G B三种颜色。第i个数字的含义是,i前面有多少个人的帽子和第i个是同样颜色。现在问,有多少种不同的帽子颜色排列方案。

解题思路:

题目很明显是一道排列数学问题,然后n的范围是1e5,所以我们考虑能不能线性解决。我们考虑状态,当我们遍历到第i个的时候,与前面有多少种不同的衔接方法。什么意思呢?考虑数列0 0 1 1,比如第三个位置的1,它可以和前面的两个零都可以结合,所以走到1这里,我们考虑把答案乘以2,走到第四个位置的1时候,这个1只能和1个0结合,所以答案乘以1. 那么,我们怎么知道可以和前面的几个数结合呢?这里就涉及到一个难点,一种准确的状态设计,使得可以每走一步也就是遍历到新的元素时,可以转移且可以告诉这个元素,你能够衔接哪几个。现在定义状态:(xi,yi,zi),我们考虑当走到第i时候某种排列的可能,更准确来说,xi表示走到第i个时候 最多相同元素的个数。yi表示走到第i个时候 次多相同元素的个数。zi 表示走到第i个时候 最少元素的个数。

那么,我们每次走到一个新的位置i时候,我们可以遍历状态state (xi,yi,zi),然后我们可以另ai和state中与ai相同的元素+1,这表明我们现在有一种可能性是这个队列这个第i个位置的人排在他的后面。

有了这个状态之后 关于答案的计算,我们可以统计ai和state中相同元素的个数有x个,然后让答案乘以x就可以了。因为这里表明我的新加进来的人有两种可能排法。

在这里我们也复习了组合数学 乘法原理的使用。乘法原理每次我们都需要模拟这一步的走法,再把每一步的走法的可能性乘起来。每一步的走法我们都可以在草稿纸上模拟。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf =1e18;
const int MODN=1000000007;
int32_t main(){
	int n;cin>>n;
	vector<int> arrmv(n);
	for(int i=0;i<n;i++)cin>>arrmv[i];
	vector<int> C(3,0);
	int ans=1;
	for(int i=0;i<n;i++){
		int cnt=0;int id=-1;
		for(int j=0;j<3;j++){
		if(arrmv[i]==C[j])cnt++,id=j;}
		if(id==-1){
			cout<<0<<endl;
			return 0;
		}
		C[id]++;
		ans*=cnt;
		ans%=MODN;
	}
		cout<<ans<<endl;
	return 0;
}
发布了171 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/FrostMonarch/article/details/103359144