Codeforces166E

这是本人写的第一次博客,学了半年的基础C语言,初学算法,若有错误还请指正。题目链接:


  
  
   
    //http://blog.csdn.net/nolanddream/article/details/44086021
  
  
http://codeforces.com/contest/166/problem/E E. Tetrahedron time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output You are given a tetrahedron. Let's mark its vertices with letters A, B, C and D correspondingly. An ant is standing in the vertex D of the tetrahedron. The ant is quite active and he wouldn't stay idle. At each moment of time he makes a step from one vertex to another one along some edge of the tetrahedron. The ant just can't stand on one place.You do not have to do much to solve the problem: your task is to count the number of ways in which the ant can go from the initial vertex D to itself in exactly n steps. In other words, you are asked to find out the number of different cyclic paths with the length of n from vertex D to itself. As the number can be quite large, you should print it modulo 1000000007 (109 + 7).InputThe first line contains the only integer n (1 ≤ n ≤ 107) — the required length of the cyclic path.OutputPrint the only integer — the required number of ways modulo 1000000007 (109 + 7).Sample test(s)input2output3input4output21NoteThe required paths in the first sample are:D - A - DD - B - DD - C - D笔者高中的时候记得在数学试卷上做过类似的题目, 不过当时是求概率的,所以笔者一看到这道题目的时候就是往概率上想,而走n步的情况总可能就是3的n次方,所以只要求出走n步到达D点的概率,用它乘以总可能的情况数并对10的9次方加7取余便是最终的答案,这里设P(A, n)为第n次到A点的概率,所以P(A, n) + P(B, n) + P(C, n) + P(D, n) = 1; 且由于对称性可以知道,P(A, n) = P(B, n) = P(C, n), 即P(A, n) = 1/3 * (1 - P(D, n))(1),而第n次到达D点,说明第n - 1次在A, B, C 中的其中一点,而又由于对称性可得,P(D, n) = 1/3*(P(A, n - 1) + P(B, n - 1) + P(C, n - 1)) = P(A, n - 1); 与(1)式联立得P(D, n + 1) = 1/3 * (1 - P(D, n)); 利用高中数列知识可以求得;则第n次到达D点的方法数等于总数(3 ^ n)乘以概率,设第n次到达D点的方法数为dp[n], 则有;这个可以直接用暴力写,也可以用矩阵快速幂写,在这就不贴代码了,不过高中学过数学竞赛的同学可能会觉得这个式子结构()有点眼熟, 3和-1的次数都是等次幂,让人联想到线性递推数列的求解 : 对于数列An = p * An-1 + q *An-2, 其特征方程为x^2 = px + q, 假设其两根分别为x1, x2,则(x1, x2不相等时)或(x1 = x2时),a,b为常数,可由给出条件获得,所以可以把中的3和-1分别看成x1,x2,由二次方程(设为x^2 + px + q = 0)的韦达定理的p = -(x1 + x2) = -2, q = x1 *x2 = 3;即方程为x^2 - 2x - 3 = 0,而此方程为数列An = 2 * An-1 + 3 *An-2的特征方程,由此我们可以得到一个递推关系式为dp[n] = 2 * dp[n - 1] + 3 *dp[n - 2];下面附上代码:[cpp] view plain copy

#include 
  
  
   
     
#include 
   
   
    
      
const int N = 1e7 + 5;  
const int mod = 1e9 + 7;  
long long dp[N], n;  
void init(){  
    dp[1] = 0;  
    dp[2] = 3;  
    for(int i = 3; i < N; i ++){  
        dp[i] = 2 * dp[i - 1] + 3 * dp[i - 2];  
        dp[i] %= mod;  
    }  
}  
int main(){  
    init();  
    while(scanf("%d", &n) == 1)  
        printf("%d\n", dp[n]);  
    return 0;  
}  


不过刚开始这点思路非常复杂,所有笔者就去想dp[n] = 2 * dp[n - 1] + 3 *dp[n - 2]的含义
在纠结了好些时辰之后,终于想通了,我们可以第n - 1步到达D点的情况中把最后一步移到其他2个点,再加一步到D点(假如说本来第n - 1步到达D点的情况为A ->D, 那么先让A走到B或C点再走到D点而此时的补数即为n), 而在n - 2 步移到D点的情况中则可以移到其他三点。
其实我们没有必要开这么大的数列对a, b每次做不同的更新就行了,就是时间会比较费:
 [cpp] view plain copy 
#include 
    
    
     
       
#include 
     
     
      
        
#include 
      
      
        using namespace std; const int mod = 1e9 + 7; int n; long long a, b; int main(){ while(scanf("%d", &n) == 1){ if(n < 2) printf("0\n"); else if(n == 2) printf("3\n"); else{ a = 0; b = 3; for(int i = 1; i <= n - 2; i ++){ if(i & 1) a = (3 * a + 2 * b)%mod; else b = (3 * b + 2 * a)%mod; } if(n & 1) printf("%I64d\n", a); else printf("%I64d\n", b); } } return 0; } 还有一种思路:假设dp[i][j] 表示第i步走到j点(用0表示D点,1,2,3分别表示A,B,C三个点)则有转移方程: dp[i][j] += dp[i - 1][k] (k, j 不相等),初始化为dp[1][1] = dp[1][2] = dp[1][3] = 1; [cpp] view plain copy 
      
     
     
    
    
   
   
  
  
#include 
   
   
    
      
#include 
    
    
     
       
typedef long long lld;  
const int N = 1e7 + 5;  
const int mod = 1e9 + 7;  
int dp[N][5], n;  
void init(){  
    dp[1][1] = 1;  
    dp[1][2] = 1;  
    dp[1][3] = 1;  
    for(int i = 2; i < N; i ++){  
        for(int j = 0; j < 4; j ++){  
            for(int k = 0; k < 4; k ++){  
                if(k == j )  
                        continue;  
                dp[i][j] += dp[i - 1][k];  
                dp[i][j] %= mod;  
            }  
        }  
    }  
}  
int main(){  
    init();  
    while(scanf("%d", &n) == 1)  
        printf("%d\n", dp[n][0]);  
    return 0;  
}  


其实可以从这个递推关系式可以推出dp[n] = 2 * dp[n - 1] + 3 *dp[n - 2]
解:dp[n][0] = dp[n - 1][1] + dp[n - 1][2] + dp[n - 1][3] = 3 * dp[n - 1][1](A,B,C三点具有对称性)
则dp[n - 1][1] = 1/3 * dp[n][1];
       dp[n - 1][1] = 2 * dp[n - 2][1] + dp[n - 2];
联立以上两式,则可得dp[n][0] = 2 * dp[n - 1][0] + 3 *dp[n - 2][0];
即为dp[n] = 2 * dp[n - 1] + 3 *dp[n - 2];
其实再次观察,便可得出另一个关系式:
dp[n] = 3 * dp[n - 1] - 3(n为奇数时)
3 * dp[n - 1] +3(n为偶数时)
[cpp] view plain copy

     
     
#include 
      
      
       
         
#include 
       
       
        
          
const int N = 1e7 + 5;  
const int mod = 1e9 + 7;  
long long dp[N], n;  
void init(){  
    dp[1] = 0;  
    dp[2] = 3;  
    for(int i = 3; i < N; i ++){  
        if(i & 1)  
            dp[i] = 3 * dp[i - 1] - 3;  
        else  
            dp[i] = 3 * dp[i - 1] + 3;  
        dp[i] %= mod;  
    }  
}  
int main(){  
    init();  
    while(scanf("%d", &n) == 1)  
        printf("%d\n", dp[n]);  
    return 0;  
} 

       
       
      
      


     
     


    
    
   
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值