201312-4-有趣的数

题目

问题描述
  我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。
输入格式
  输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
  输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3

我的代码

#include <iostream> 
#include <cstring> 
using namespace std;
long long dps[4];//因为题目中明确可能性会超过十亿零七,所以用long long数据类型存储,该数据类型取值范围为-2^63~2^63-1,可以承受
long long dpsq[1001][2][2][2][2][2][4];
long long dp(int n,bool flag,int branch) {
	int flag0=(dps[0]>0),flag1=(dps[1]>0),flag2=(dps[2]>0),flag3=(dps[3]>0);
	long long ans=0;
	if(dpsq[n][flag0][flag1][flag2][flag3][flag][branch]!=-1)
		return dpsq[n][flag0][flag1][flag2][flag3][flag][branch];
	if(n<4&&flag)
		return 0;
	if(n<=1)
	{
		if(dps[0]>0&&dps[1]>0&&dps[3]>0&&n==1)
			return 1;
		else
			return 0;
	}
	else
	{
		if(flag)
		{
			ans+=2*dp(n-1,true,0);
			dps[1]++;
			ans+=dp(n-1,false,1); 
			dps[1]--;
			dps[3]++;
			ans+=dp(n-1,false,3); 
			dps[3]--;
		}
		else
		{
			if(branch==1)
			{
				if(dps[3]>0)
				{
					dps[2]++;
					ans+=dp(n-1,false,branch); 
					dps[2]--;
				}
				if(dps[2]==0)
				{
					dps[3]++;
					ans+=dp(n-1,false,branch); 
					dps[3]--;
				}
				dps[0]++;
				ans+=dp(n-1,false,branch); 
				dps[0]--;
			}
			else
			{
				if(dps[1]>0)
				{
					dps[0]++;
					ans+=dp(n-1,false,branch); 
					dps[0]--;
				}
				if(dps[0]==0)
				{
					dps[1]++;
					ans+=dp(n-1,false,branch); 
					dps[1]--;
				}
				dps[2]++;
				ans+=dp(n-1,false,branch); 
				dps[2]--;
			}
		}
	}
	ans %= 1000000007;
	dpsq[n][flag0][flag1][flag2][flag3][flag][branch]=ans;
	return ans;
}
int main() {
	int n=false;
	cin >> n;
	dps[0]=dps[1]=dps[2]=dps[3]=0;
	memset(dpsq,-1,sizeof(dpsq));
	long long ans = dp(n,true,0);
	cout << ans << endl;
	return 0;
}

我的思路

基本思路是动态规划,因为考虑到最终结果肯定超过10亿,然后如果用现有计数器一个个加的方法,要至少加10亿次,所以如果不用动态规划肯定超时。
然后对于一个n位有趣的数,递归考虑的话

  1. 可以由一个n-1位有趣的数加3或者1组成
  2. 可以由一个n-1位缺乏1或者缺乏3但是满足规则顺序的数加上1或者3组成

在这种情况下,结合动态规划的思想,我要考虑把递归函数的结果存储起来,然后再考虑递归过程
这里有点些许复杂的是n-1位非递归的数的递归思路,这里比较容易推出,画递归树可以清晰得到,不赘述
然后结合递归树,得到整个代码的递归过程
递归出口:

  • 之前已经计算过的函数值
  • 当n比4小同时计算的是有趣的数,4位以下有趣的数不存在,返回0
  • 当n递归到1,结束,这时如果监测到除了第一位后面的数位除了2都齐全,就返回1,因为最高位经过分析只能为2,否则返回0,不满足4个数至少出现一次的规则

递归过程

  • 第一条路,要计算有趣的数
  • 第二条路,计算非有趣的数走不存在1的分支
  • 第三条路,计算非有趣的数走不存在3的分支

这道题我的思路解出来AC好像要快一些,显示是0ms,但是有点疑惑到底是的确是快,还是其他原因,这一点有没有知道的人希望评论区告诉我,谢谢

参考答案及注释解析

#include <iostream> 
#include <cstdio> 
#include <cstring> 
#include <string> 
#include <vector> 
#include <deque> 
#include <list> 
using namespace std;
long long f[2000][3][2];//因为题目中明确可能性会超过十亿零七,所以用long long数据类型存储,该数据类型取值范围为-2^63~2^63-1,可以承受
int dp(int n, int p1, int p3) {
	long long &now = f[n][p1][p3];
	if (now != -1)   return now;
	if (n == 0) {
		if (p1 == 2 && p3 == 1) {
			now = 1;
		} else {
			now = 0;
		}
		return now;
	}
	now = 0;
	if (p1 == 0) {
		now +=  dp(n-1, 1, p3);
	} else if (p1 == 1) {
		now += dp(n-1, 1, p3);
		now += dp(n-1, 2, p3);
	} else {
		now += dp(n-1, 2, p3);
	}
	if (p3 == 0) {
		now += dp(n-1, p1, p3);
		now += dp(n-1, p1, 1);
	} else {
		now += dp(n-1, p1, 1);
	}
	now %= 1000000007;
}
int main() {
	int n;
	cin >> n;
	memset(f, -1, sizeof(f));
	//初始化三维数组f,每个元素都为-1
	int ans = dp(n - 1, 0, 0);
	cout << ans << endl;
	return 0;
}
发布了18 篇原创文章 · 获赞 0 · 访问量 410

猜你喜欢

转载自blog.csdn.net/qq_41985293/article/details/104109085