题目:ACM入场券
【问题描述】
ACM协会举办第1次活动,要为每个队员发入场卷,入场卷分别有A、C、M三种,每个人可以任意拿其中的一种,排队入场,要求每相邻两个不能同时为A;
例如:只有一个人排队,则有三种即:A、C、M
两个人排队,则有八种即:AC、AM、CA、CC、CM、MA、MC、MM
输入n个人,计算有多少种不同的方案,可能数很大,结果用1000007取余
【输入格式】
一个正整数n:表示入场的人数
【输出格式】
输出:方案数m
【样例输入1】
1
【样例输出2】
3
【样例输入2】
2
【样例输出2】
8
【数据规模】
规模:3<=N<1000000
算法分析:
1.首先,根据其规模可知,不能用常规的搜索来做,也无法用贪心,都会超时,因此就可以先往动态规划这方面思考。
2.dp[i][j]定义:这里我们就直接先定义:
dp[1000010][3] ,其中分为以下三种情况:
(1)表示前i个人排队时,最后一个人选择A的方案数(含A的个数);
则 dp[i][0] = dp[i-1][1]
对于推导 dp[i][0]
的逻辑,根据题目要求,相邻的两个人不能同时选择A。因此,对
于第i个人选择A的情况,只能由前一个人不选择A的方案数推出。因为如果前一个人选
择A,则第i个人不能选择A,只能选择C或M.
所以应该为:
dp[i][0] = dp[i-1][1]
(2)表示前i个人排队时,最后一个人不选择A的方案数(不含A的个数);
dp[i][1] 表示不含A的个数,根据题目要求,相邻的两个人不能同时为A。
因此,对于第 i 个人不含A的情况,可以有两种情况:
1. 第i-1个人选择A,第i个人选择C或M;
2. 第i-1个人选择C或M,第i个人选择C或M。
由于每个人可以选择C或M,所以每种情况下都有两种选择,因此需要乘以2。这样计算
出的 dp[i][1] 表示的是不含A的方案数,且满足相邻两个人不能同时为A的条件。
所以正确的的应该为:
dp[i][1] = dp[i-1][2]*2
(3)表示前i个人排队时的总方案数。
综上,便可以很轻松的得出:
dp[i][2] = dp[i] + dp[i-1]
3.dp数组的初始化:
(1)当 i 为 0 时,很明显,dp[0][j] = 0,啥也没有嘛,还装蒜😂😂,直接赋值为 0。
当然其实严格意义上讲,dp[0][j]本身也 没有意义,赋值为任何值都不影响状态转移方
程的递推程。
(2)当 i=1 时,我们也可以很轻易得到如下:
dp[1][0] = 1
dp[1][1] = 2
dp[1][2] = 3
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
// dp数组定义及初始化:
int dp[1000010][3] = {{0,0,0}, {1,2,3}};
int main(){
int i, n;
cin >> n;
// 下面每个递推公式都用 1000007 求余了,是避免在递推过程中栈溢出,
// 而且题目要求的也只是最终答案用 1000007 取余数结果而已
for(i=2; i<=n; i++){
// 前 i 的人排队,最后一个选 A 的方案数
dp[i][0] = dp[i-1][1] % 1000007;
// 前 i 的人排队,最后一个不选 A 的方案数
dp[i][1] = dp[i-1][2] * 2 % 1000007;
// 前 i 个人排队时的总方案数
dp[i][2] = (dp[i][0] + dp[i][1]) %1000007;
}
// 输出结果:前 i 个人排队时的总方案数如下
cout << dp[n][2] << endl;
return 0;
}
解法二:
其实该题也可以根据数学方法推导有出来,从 n>=3 开始有以下规律:
n = 1 , ans(1) = 3;
n = 2, ans(2) = 8;
n = 3, ans(3) = 22;
...
n时, ans(n) = ( ans(n-1) +ans(n-2) ) * 2
这里就不给大家推导了哈,大家可以去自己推导一下试试:
该方法完整代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,c,ans,n;
a=3,b=8;
cin>>n;
if(n==1) {
cout<<3;
return 0;
}
if(n==2){
cout<<8;
return 0;
}
for(int i=3;i<=n;i++){
c=((a+b)*2)%1000007;
a=b;
b=c;
}
cout<<c<<endl;
}
总结归纳:
关于这个题以及这一类型的题,咱们就关注两个点:
1.判断该题是否能用常规的搜索、贪心直接做,如果不能且没有思路就往动规上面思考。
2.确定了要用dp,就继续思考我们要设的 dp 定义是什么?(根据题目答案方面考虑,如该题我们给出的dp数组的定义,那三种情况),因为确定了、清楚了 dp 定义,我们才能进行状态转移方程的思考,不然假如随便给你一个 01 背包的状态转移方程,dp 定义都不清楚的人就更别提理解该状态转移方程了。
一般关于动态规划的题目,上面第二点考虑清楚了,状态转移方程就确定了,该题就基本可以拿下了。