很有意思的一道题

木块砌墙
题目详情:

用 1×1×1, 1× 2×1以及2×1×1的三种木块,

20130517165209_712020130517165245_7105

搭建K × 2^N × 1的墙,不能翻转、旋转(0<=N<=1024,1<=K<=5)

有多少种方案,输出结果对1000000007取模。

举例:

举个例子如给定N=1 K=2

答案是7,如下图所示

20130517171322_7190

 

add 恶补了一下动态规划,矩阵快速幂,状态压缩,学习了http://www.matrix67.com/blog/archives/276,还有http://hi.baidu.com/scameeling/item/4527cdcd0bc4851fb67a2417

唉,终于凑出来了结果。。

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;


#ifdef WIN32
#define LL __int64
#else
#define LL long long
#endif


#define SIZE (1<<k)
#define MAX_SIZE 32
using namespace std;


LL g_m;
class CMatrix
{
public:
    LL element[MAX_SIZE][MAX_SIZE];
    void setSize(int);
    void setModulo(LL);
    CMatrix operator* (CMatrix);
    CMatrix power(int);
private:
    int size;
    LL modulo;
};


void CMatrix::setSize(int a)
{
    for (int i=0; i<a; i++)
        for (int j=0; j<a; j++)
            element[i][j]=0;
    size = a;
}


void CMatrix::setModulo(LL a)
{
    modulo = a;
}


CMatrix CMatrix::operator* (CMatrix param)
{
    CMatrix product;
    product.setSize(size);
    product.setModulo(modulo);
    for (int i=0; i<size; i++)
        for (int j=0; j<size; j++)
            for (int k=0; k<size; k++)
            {
                product.element[i][j]+=element[i][k]*param.element[k][j];
                product.element[i][j]%=modulo;
            }


    return product;
}


CMatrix CMatrix::power(int exp)
{
    CMatrix tmp = (*this) * (*this);
    if (exp==1) return *this;
    else if (exp & 1) return tmp.power(exp/2) * (*this);
    else return tmp.power(exp/2);
}


LL stat[MAX_SIZE][MAX_SIZE];


void dfs(LL t,LL curRowStk,LL preRowStk,LL b1,LL b2,LL p)
{
    if (p>g_m)
    {
        if (b1==0 && b2==0)
        {
            stat[preRowStk][curRowStk]++;
        }
        return ;
    }
    if (b1==0 && b2==0)//  1
    {
        //  1
        dfs(t,curRowStk<<1|1,preRowStk<<1  ,0,0,p+1);
        dfs(t,curRowStk<<1|1,preRowStk<<1|1,1,0,p+1);
        dfs(t,curRowStk<<1|1,preRowStk<<1|1  ,0,0,p+1);
        dfs(t,curRowStk<<1  ,preRowStk<<1|1,0,0,p+1);
        return ;
    }
    if (b1==1 && b2==0)
    {
        dfs(t,curRowStk<<1|1,preRowStk<<1|1,0,0,p+1);
        return ;
    }
    dfs(t,curRowStk<<1|1,preRowStk<<1,0,0,p+1);
}




int calculate(int n,int k)
{
    g_m = k;
    memset(stat,0,sizeof(stat));
    dfs(1,0,0,0,0,1);
    CMatrix unit;
    unit.setSize(SIZE);
    for(int i=0; i<SIZE; i++)
    {
        for(int j=0; j<SIZE; j++)
        {
            unit.element[i][j]=stat[i][j];
        }
    }
    LL p = 1000000007;
    unit.setModulo(p);
    CMatrix tmp;
    if (n<1)
    {
        tmp = unit.power(1);
    }
    else
    {
        tmp = unit.power(2);
        for (int i = 0; i < n-1; i++)
        {
            tmp = tmp.power(2);
        }
    }
    return tmp.element[SIZE-1][SIZE-1];
}


int main()
{
    for (int i = 0; i<10; i++)
    {
        for (int j = 1; j < 6; j++)
        {
            LL cc = calculate(i,j);
            cout<<i<<" "<<j<<"=>"<<cc<<endl;
        }
    }
    return 0;
}



这道题可能高三的时候也许能做出来。。。排列组合和数学归纳法都忘得差不多了。

因为题目厚度为1,所以这其实是个平面的题,跟俄罗斯方块一样。

只能尝试一下。

首先我们指定K=1,因为不涉及2*1*1的,能简单一点。假设n,k的结果为f(n,k)

用程序遍历的结果如下 f(6,1)的结果已经太大,无法遍历了。

f(0,1) = 1

f(1,1) = 2 = 1^2 + 1^2

f(2,1) = 5 = 1^2 + 2^2

f(3,1) = 34 = 3^2 + 5^2

f(4,1) = 1597 = 21^2 + 34^2

f(5,1) = 3524578 = 987^2 + 1597^2

很明显,f(n,1) 和f(n-1,1)是有很明显的关系的,那么这个987又是怎么来的呢

987 = 21*47 = 21*(34-21+34)

因此我们猜想

a = f(n,1)

b= f(n-1,1)

c = sqrt(a-b*b)

f(n+1,1) = a*a + [c*(b-c+b)]*[c*(b-c+b)]。

现在的脑子已经无力对此化简直接求f(n,1)了,应该是个复杂的指数函数吧。

明天有空在看一下k对f(n,k)的影响。

N:0 K:1-> 1
N:0 K:2-> 2
N:0 K:3-> 3
N:0 K:4-> 5


N:1 K:1-> 2
N:1 K:2-> 7
N:1 K:3-> 22
N:1 K:4-> 71


N:2 K:1-> 5
N:2 K:2-> 71
N:2 K:3-> 823
N:2 K:4-> 10012


N:3 K:1-> 34
N:3 K:2-> 7573
N:3 K:3-> 1222550
N:3 K:4-> 211351945

粗略的接近等比关系,但是不精确,很难猜了。

看了

http://www.matrix67.com/blog/archives/276

的问题9,感觉代码的方向不太对,但是感觉跟2*1还是有很大区别。

这原来是个动态规划,涉及矩阵,矩阵快速幂,状态转移等概念。

先看看书再说,

穷举的代码如下:

#include <iostream>
#include <math.h>
using namespace std;


long long total = 0;
int brick[1024*5];


void CountBrick(int deep,int n,int k)
{
if (deep >= k*n)
{
  total++;
  return;
}
if (brick[deep] == 0)
{
brick[deep] = 1;
CountBrick(deep+1,n,k);
brick[deep] = 0;
if ((deep+1)%k > 0 && (deep+1)<k*n)
{
if (brick[deep+1] == 0)
{
brick[deep] = 1;
brick[deep+1] = 1;
CountBrick(deep+2,n,k);
brick[deep] = 0;
brick[deep+1] = 0;
}
}
if (deep+k < k*n)
{
if (brick[deep+k] == 0)
{
brick[deep] = 1;
brick[deep+k] = 1;
CountBrick(deep+1,n,k);
brick[deep] = 0;
brick[deep+k] = 0;
}
}
}
else
{
CountBrick(deep+1,n,k);
}
}


int Answser(int n,int k)
{
total = 0;
CountBrick(0,pow((double)2,n),k);
return total;
}


int main()
{
for (int i = 0; i< 1024*5; i++)
  {
    brick[i] = 0;
  }
  for (int i = 0;i < 4;i++)
  {
  for (int j = 1; j < 5; j++)
  {
  cout<<"N:"<<i<<" K:"<<j<<"-> "<<Answser(i,j)<<endl;
  }
  cout<<endl;
  }


}






 

转载于:https://my.oschina.net/meizhitu/blog/132239

以下是一些C语言初学者可能会觉得有趣的编程: 1. 猜字游戏:编写一个程序,让用户猜一个范围内的随机生成的整,直到猜中为止。程序应根据用户的猜测给出提示,告诉用户是猜大了还是猜小了。 2. 九九乘法表:编写一个程序打印出九九乘法表,即从1乘1到9乘9的所有结果。 3. 排序算法:实现常见的排序算法,如冒泡排序、选择排序或插入排序,并通过程序验证其正确性。 4. 计算器:编写一个简单的计算器程序,可以接受两个操作一个运算符,然后根据运算符执行相应的运算并输出结果。 5. 石头剪刀布游戏:编写一个石头剪刀布游戏的程序,让用户选择石头、剪刀或布,并与计算机进行对战,判断输赢并输出结果。 6. 简易登录系统:编写一个简单的登录系统,要求用户输入用户名和密码,并进行验证,如果匹配则输出登录成功,否则输出登录失败。 7. 生成斐波那契列:编写一个程序,使用循环或递归方式生成斐波那契列的前n项,并输出结果。 8. 统计字符个:编写一个程序,接受一个字符串和一个字符,统计该字符在字符串中出现的次,并输出结果。 9. 找出最大值和最小值:编写一个程序,接受一组整输入,并找出其中的最大值和最小值,并输出结果。 10. 简易图书管理系统:编写一个简易的图书管理系统,包括图书的增加、删除、查询等功能,并可以将图书信息存储在文件中。 以上这些编程可以帮助初学者巩固基本的编程知识和技能,并提高对C语言的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值