FOJ 1683 纪念SlingShot

       来源:http://acm.fzu.edu.cn/problem.php?pid=1683

       概述:已知 F(n)=3 * F(n-1)+2 * F(n-2)+7 * F(n-3),n>=3,其中F(0)=1,F(1)=3,F(2)=5,对于给定的每个n,输出F(0)+ F(1)+ …… + F(n) mod 2009。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

理论分析


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

编程分析

       (1)要写一个矩阵相乘函数MUL(int a[][N], int b[][N], int c[][N]),其中N为宏定义——#define N 4,代表其为四阶方阵。该函数将矩阵a*b的结果保存在c矩阵。

       (2)其次,要为写一个快速幂模函数POW(int b, int P[][N]),参数b代表其次幂数n-2,参数P用来保存最后结果的返回值。

       (3)矩阵赋值,可以使用memcpy函数完成。

#include <cstdio>
#include <cstring>
using namespace std; 
#define MOD 2009
#define N	4
const int NUM = sizeof(int)*N*N;

void MUL(int a[][N], int b[][N], int c[][N])
{
	int i, j;
	for (i = 0; i < N; i++) for (j = 0; j < N; j++)
		c[i][j] = (a[i][0]*b[0][j] + a[i][1]*b[1][j]
			+ a[i][2]*b[2][j] + a[i][3]*b[3][j]) % MOD;
}

void POW(int b, int P[][N])
{
	int A[N][N] = {{1,3,2,7},{0,3,2,7},{0,1},{0,0,1}}, T[N][N];
	
	memset(P, 0, NUM);
	for (int i = 0; i < N; i++) P[i][i] = 1;

	while (b)
	{
		if (b&1)
		{
			MUL(P, A, T);
			memcpy(P, T, NUM);
		}
		b >>= 1;
		MUL(A, A, T);
		memcpy(A, T, NUM);
	}
}

int main()
{
	int t, T, n, P[N][N], ans;
	scanf("%d", &T);
	for (t = 1; t <= T; t++)
	{
		scanf("%d", &n);
		if (n == 0) ans = 1;
		else if (n == 1) ans = 4;
		else
		{
			POW(n-2, P);
			ans = (9*P[0][0]+5*P[0][1]+3*P[0][2]+P[0][3]) % MOD;
		}
		printf("Case %d: %d\n", t, ans);
	}
	return 0;
}

       花絮:写完后太开心,着急着提交,没想到既然会TLE,太伤心了,我觉得我的优化水平只能到如此,这样都TLE,这题我无能为力了。然后才发觉自己n=0和n=1的特殊情况忘记处理。。。改完后,就用281ms AC了。再分析后,我发现我的代码确实还能再优化,就是接下来补充内容讲到的MUL函数。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

补充:关于MUL函数效率

       有些人会写一个Matrix的结构体,然后重载*运算符为矩阵乘法。但我觉得那样写,代码不够精简,直接用数组存储矩阵,然后写这样一个MUL函数也是很方便使用的。

       在MUL函数里,矩阵相乘一般是要三层循环的,那样的话,使用c数组前要初始化所有元素为0(可以使用memset函数),像这段代码:

void MUL(int a[][N], int b[][N], int c[][N])
{
	int i, j, k;
	memset(c, 0, NUM);
	for (i = 0; i < N; i++)
		for (j = 0; j < N; j++)
		{
			for (k = 0; k < N; k++)
			{
				c[i][j] += a[i][k]*b[k][j];
			}
			c[i][j] %= MOD;
		}
}
       这样付出的效率代价是很高的,用这个MUL函数求解该题,需要218ms。当矩阵阶数不是特别大时,去掉第三层的k循环,将式子展开赋值,上述题目这样操作仅需109ms,效率提高了一倍。

       有时候数值比较大,怕溢出时,则不得不用三层循环的MUL,要注意把代码写成这样:

void MUL(int a[][N], int b[][N], int c[][N])
{
	int i, j, k;
	memset(c, 0, NUM); 
	for (i = 0; i < N; i++)
		for (j = 0; j < N; j++)
			for (k = 0; k < N; k++)
			{
				c[i][j] = (c[i][j] + a[i][k]*b[k][j]) % MOD;
			}
}
       这时候MOD写在最内层,效率又会下降,这题采用该代码,用时281ms。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值