骑士

来源:互联网 发布:windows 域控 编辑:程序博客网 时间:2024/06/10 06:32

问题描述 Problem Description

国际象棋中骑士的移动规则和中国象棋中的马是类似的, 它先沿着一个方向移动两格,再沿着与刚才移动方向垂直的方向移动一格。 路径上的棋子并不会影响骑士的移动,但是如果一个骑士走到了一个放有棋子的格子,它就会攻击那个棋子。 现在有一个 nn 的棋盘,有k 个骑士需要被摆到棋盘上去。那么使所有骑士互不攻击的摆放方式一共有多少种呢?


输入描述 Input Description

一行: 两个整数, n,k


输出描述 Output Description

一行: 一个整数,为摆放的方式数


输入样例 Sample Input

[1]

3 2

[2]

4 4


样例输出 Sample Output

[1]

28

[2]

412


数据范围及提示 Data Size & Hint

1n8,1kn2


分析 Code

状态压缩,记 fi,j,k,l 表示在前 i 共摆放 j 个马,且第 i1 行的状态为 k ,第 i 行的状态为 l ,如果存在状态 r 可以摆在第 i+1 行,则 fi,j+Ar,l,r+=fi,,j,k,l,其中 Ar 表示 r 的二进制中 1 的个数。理论时间复杂度为 O(nm23n)=O(nm8n) ,会超时,但实际上 fi,j,k,l 中会存在大量的 0 ,处理一下就不会超时了。


代码 Code

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long LL;bool link1[1<<8][1<<8];bool link2[1<<8][1<<8];LL f[9][33][1<<8][1<<8];int cnts[1<<8];int n,m;int cnt(int);bool check1(int,int);bool check2(int,int);int main(){    scanf("%d%d",&n,&m);    if(m > 32){        printf("0");        return 0;    }    for(int i=0;i<(1<<n);++i)        cnts[i] = cnt(i);    for(int i=0;i<(1<<n);++i)        for(int j=i;j<(1<<n);++j){            link1[j][i] = link1[i][j] = check1(i,j);            link2[j][i] = link2[i][j] = check2(i,j);        }    f[0][0][0][0] = 1;    for(int i=0;i<n;++i)        for(int j=0;j<=m;++j)            for(int k=0;k<(1<<n);++k)                for(int l=0;l<(1<<n);++l)                    if(f[i][j][k][l])                        for(int r=0;r<(1<<n);++r)                            if(link1[l][r] && link2[k][r] && j+cnts[r]<=m)                                f[i+1][j+cnts[r]][l][r] += f[i][j][k][l];    LL ans = 0;    for(int i=0;i<(1<<n);++i)        for(int j=0;j<(1<<n);++j)            ans += f[n][m][i][j];    printf("%lld\n",ans);    return 0;}int cnt(int x){    int ans = 0;    while(x){        x -= x&-x;        ++ans;    }    return ans;}bool check1(int i,int j){    int k;    while(i){        k = i&-i;        if(j&(k<<2) || j&(k>>2))            return false;        i -= k;    }    return true;}bool check2(int i,int j){    int k;    while(i){        k = i&-i;        if(j&(k<<1) || j&(k>>1))            return false;        i -= k;    }    return true;}
0 0
原创粉丝点击