百度之星冬季邀请赛第二题

du熊填数字
Time Limit: 3000/2000 MS (C/Others) Memory Limit: 65536/32768 K (C/Others)

本次组委会推荐使用C、C++Problem Description 

原题:http://tieba.baidu.com/p/2039526366


du熊这几天使劲的往一个n 行n列的矩阵填0和1这两个数字,n为偶数,而且矩阵由里向外分成了n / 2层。比如n = 6时,矩阵的分层如下:


du熊填数时有一个要求:不能存在两个相邻的1,且位于不同的层(这里的相邻指两格子共用一条线)。 请你帮du熊计算一下有多少种填法。

Input 输入包含多组测试数据,每组数据包含一个偶数n (2 <= n <= 500)。

Output 请计算并输出对2012取余后的结果。

Sample Input 2 4S

ample Output 16 1952 

Hint当n = 4时 1011 0100 0100 0000是满足要求的 1111 0100 0100 0000是不满足要求的,因为第一行第二列的1和第二行第二列的1相邻且位于不同的层。



我的想法:

开始的时候,发现四个角上的四块是绝对独立的,与其他的无关。

首先,我先把正方形按照对角线分成四块相对独立直角三角形看待的,每个三角(只是看作三角)中的方块都与固定方向相邻的比较(我这里的意思是,上下两个三角形里的正方块上下比较,左右的左右比较),发现这样根本不能解决问题,因为对角线上的方块根本不允许这样做,更重要的是这样子只是减少了穷举而已,肯定绝对一定会超时,所以放弃。

then。

由于每个正方体的边数都是偶数(n),所以可以看作独立的四块,分成4个以(n/2)为边的正方体,每个正方体都是独立的。我发现了一个很简单的规律,拿左上角的大正方体举例,左上角的一个方块是独立的,接下来的与它相挨的三个方块,这三个方块作为一个整体是独立的,他们三个的01分布跟其他部分无关,然后是与这三个相挨的5个……一直到n-1个,就是1,3,5,7,9,...,n-1,堑把这称作“层”。这时问题就转换成了在01限制下,奇数连续的填法。填法其实很简单,把1和3 的列出来就能发现规律了,1的时候是两种"1","0",3的时候可以看作在1的情况下追加两个数,以后的每个奇数都可以看作上一个奇数的追加两个数,这是个很简单的规律,拿3举例,在"1"后面追加有"00""01"两种情况,在"0"后面追加有"00""01""10"三种情况,这就很明显了,我就不继续往下分析了,当这个奇数是2i-1时(第i个奇数时),填法是3^(i-1)+2^(i-1)种(^就是幂,额),因为每层都是独立的,所以从1到n-1运算出来的结果相乘,再四次幂就是问题的答案了。

对2012取余,上面这个方法涉及的都是加法或者乘法,所以取余没有限制,那么就要选择在恰当的时候对2012取余,因为取余的算法相当于>>>>a%b=a-a/b*b;所以处处都取余是不可取的,我的做法是在定义pow数组的时候取余,我认为这很必要,再就是每次大乘一下都取余一次,我没有验证是否高效。




呼,描述出自己想的真不那么简单啊,还是觉得描述的不那么清晰,毕竟是第一次,见谅。有什么不足,望指点。

下面是我的答案:

#include<iostream>
using namespace std;

int main()
{
	int n;
	int	pow2[2012];//2的n次方对2012取余
	int pow3[2012];//3的n次方对2012取余
	pow2[0]=1;
	pow3[0]=1;
	//初始化那俩pow
	for(int i=1;i<2012;i++)
	{
		pow2[i]=(pow2[i-1]<<1)%2012;
		pow3[i]=(pow3[i-1]*3)%2012;		
	}
	while(cin>>n)
	{
		if(n==0)
		{
			cout<<"0"<<endl;
			continue;
		}
		int num=n>>1;
		int sum = 1;
		int temp;
		for(int i = 0;i<num;i++)
		{
			temp = pow2[i] + pow3[i];
			sum*=temp;
			sum%=2012;
		}
		sum*=sum;
		sum%=2012;
		sum*=sum;
		sum%=2012;
		cout<<sum<<endl;
	}
	return 0;
}

你能发现这个算法的错误在哪儿吗?啊哈



错误出在每次追加两个位数的时候,我把以0结尾的乘2,以1结尾的乘2,其实以0结尾的产生三倍的数,其中三分之二仍以0结尾,另外三分之一以1结尾;而以1结尾的产生两倍于自身的数 产生的数里面以0和1结尾的各一半。可以归纳成斐波那契似的递归规律。


A(1) = 2

A(2) = 5

.

.

A(n) = 3A(n-1) - A(n-2)


B(n) = [A(1)· A(2)····A(n)]^4 mod 2012


#include<iostream>
using namespace std;

int main()
{
	int n;
	int	a[501];
	int b[501];
	a[1]=2;
	a[2]=5;
	b[0]=0;
	b[1]=2;
	b[2]=10;
	//初始化那a
	for(int i=3;i<501;i++)
	{
		a[i]=(a[i-1]*3 - a[i-2])%2012;
		b[i]=(b[i-1]*a[i])%2012;
	}
	while(cin>>n)
	{
		n>>=1;
		int sum = (b[n]*b[n])%2012;
		sum*=sum;
		sum%=2012;
		cout<<sum<<endl;
	}
	return 0;
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值