hdu 6253 Knightmare - CSDN博客

文章价值(3) ?
阅读数(510) ?
A knight jumps around an infinite chessboard. The chessboard is an unexplored territory. In the spirit of explorers, whoever stands on a square for the first time claims the ownership of this square. The knight initially owns the square he stands, and jumps N times before he gets bored.
Recall that a knight can jump in 8 directions. Each direction consists of two squares forward and then one squaure sidways.

After N jumps, how many squares can possibly be claimed as territory of the knight? As N can be really large, this becomes a nightmare to the knight who is not very good at math. Can you help to answer this question?

Input

The first line of the input gives the number of test cases, T. T test cases follow.
Each test case contains only one number N, indicating how many times the knight jumps.
1≤T≤105
0≤N≤109

Output

For each test case, output one line containing “Case #x: y”, where x is the test case number (starting from 1) and y is the number of squares that can possibly be claimed by the knight.

Sample Input

3
0
1
7

Sample Output

Case #1: 1
Case #2: 9
Case #3: 649

【题意】

在一个无限大的棋盘里面玩游戏,你初始的时候有一匹马,问你经过k步或者经过少于k步,马的可能位置有哪些。

【分析】

首先看到这个数据就显然可以得到,这个题目显然是有规律的,不然一定不能做。首先肯定是打表找规律啊。

【打表代码】

#include
using namespace std;
bool mat1[1005][1005];
bool mat2[1005][1005];
int changex[]={0,2,2,1,1,-1,-1,-2,-2};
int changey[]={0,1,-1,2,-2,2,-2,1,-1};
int main()
{
//    freopen("in.txt","r",stdin);
    mat1[500][500] = true;
    ans0[0] = 1;
    for(int i = 1; i<=200; i++)
    {
        int ans = 0;
        if(i&1)
        {
            memset(mat2,false,sizeof(mat2));
            for(int x = 0; x<1005; x++)
                for(int y = 0; y<1005; y++)
                {
                    if(mat1[x][y])
                        for(int l = 0; l<9; l++)
                            mat2[x+changex[l]][y+changey[l]] = true;
                }
            for(int x = 0; x<1005; x++)
                for(int y = 0; y<1005; y++)
                    if(mat2[x][y])
                        ans++;
        }
        else
        {
            memset(mat1,false,sizeof(mat1));
            for(int x = 0; x<1005; x++)
                for(int y = 0; y<1005; y++)
                {
                    if(mat2[x][y])
                        for(int l = 0; l<9; l++)
                            mat1[x+changex[l]][y+changey[l]] = true;
                }
            for(int x = 0; x<1005; x++)
                for(int y = 0; y<1005; y++)
                    if(mat1[x][y])
                        ans++;
        }
        cout<<>return 0;
}

【接着分析】

然后你就显然可以得到的结论是,这个规律不好找(数学好的大佬请忽略)。尤其是里面有质数,显然不是一个很好的东东。于是,第一次做这个题目的时候我是直接把表贴到了OEIS上,然后就没有然后了。神器就是神器。

然后在学长的鼓舞下,我第二天又分析了一波,其实,规律也没那么麻烦。

【三次分析】

首先,这个增长的数量级显然是好分析出来的,是一个n方级别的增长。接下来就是数学推导了。n方级别的,我们就用n方级别的函数来拟合。然后,第一,存储原来的答案在ans0里面表示ans的0阶差分,ans1表示ans0的差分,也就是ans的一阶差分,ans2表示ans1的差分,也就是ans0的二阶差分。然后规律就很明显了。大概就是这个过程:

    ans0[i] = ans;
    for(int i = 1;i<=200;i++)
    {
        ans1[i] = ans0[i] - ans0[i-1];
    }
    for(int i = 2;i<=200;i++)
    {
        ans2[i] = ans1[i] - ans1[i-1];
    }

然后把ans2输出一下就会发现很明显的规律。就是

ans2[2] = 24
ans2[3] = 36
ans2[4] = 28
ans2[5] = 24
ans2[6-inf] = 28

也就是说当n大于等于6的时候ans1就是一个线性增长。也就是说当n>=6的时候可以得到

ans1[n] = (n-5)*28+120 = 28*n-20

从而可以总结出ans1的公式就是:

ans1[1] = 8
ans1[2] = 32
ans1[3] = 68
ans1[4] = 96
ans1[5-inf] = 28*n-20

然后显然可以得到ans0的规律。这里我简单的推倒一下:

当n>=5时有:ans1[n] = ans0[n] - ans0[n-1] = 28*n-20
所以有:
    ans0[n] - ans0[n-1] = 28*n-20
    ans0[n-1] - ans0[n-2] = 28*(n-1)-20
    ......
    ans0[6] - ans0[5] = 28*6 - 20
    ans0[5] - ans0[4] = 28*5-20
然后将上式累加可以得到:
    ans0[n] = 14*n^2 - 6*n + 5   (n>=5)
然后我们带入我们最开始打的表可以验证上式的正确性。然后对于小于5的特判一下就ok。
最后唯一一个比较坑人的地方是14*1e9*1e9爆掉了long long但是没有爆掉unsigned long long,

【代码】

#include 
using namespace std;
int main()
{
//    freopen("in.txt","r",stdin);
    int T;
    cin>>T;
    for(int i = 0;icout<<"Case #"<< ++i <<": ";
        unsigned long long n;
        cin>>n;
        if(n==0) cout<<1<else if(n==1) cout<<9<else if(n==2) cout<<41<else if(n==3) cout<<109<else if(n==4) cout<<205<else 
        {   
            cout<<14*n*n - 6*n + 5<return 0;
}
#define wa  accept
#define re  accept
#define tle accept
#define mle accept
#define pe  accept




(注:此网站文章大都来自公开的博客,如有原作者发现有侵权问题,请在此留言,我们会立刻删除相关文章。)

我的评价: