[51nod1142] 棋子遍历棋盘
2017-02-23 16:26
239 查看
题目大意
一个M*N的棋盘, 有一个棋子每次可以向上下左右4个方向中的1个走1步,让这个棋子从(1,1)位置出发,走遍所有格子恰好1次,最后回到(1,1),有多少种不同的走法。由于方案数量巨大,输出数量Mod 10^9 + 7即可。M≤109
N≤5
分析
题目相当于求一条哈密顿回路,那么可以不用管哪里出发(这个很显然)发现N很小!很自然地可以往插头DP方面想。用括号序表示插头的联通状态即可。(手算一下发现当n=5,有用的状态只有21个)。
M却很大,但是21 * 21 * 21很小,所以可以矩阵乘法优化。
注意:由于是一条回路,在中间的部分不能转移到插头状态#####,只有最后f[m]
才能转移到#####,也就是做矩阵乘法求出f[m-1]
,然后最后一行暴力。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=113,mo=1e9+7; typedef long long LL; int n,m,p,q,ans,id[799],tot,Sta[25]; struct node { int H[maxn],tot,st[maxn],s[maxn]; void init() { memset(H,255,sizeof(H)); tot=0; } void add(int S,int v) { int i=S%maxn; for (;H[i]>=0 && st[H[i]]!=S;i=(i+1)%maxn); if (H[i]<0) { st[tot]=S; s[tot]=v; H[i]=tot++; }else s[H[i]]=(s[H[i]]+v)%mo; } }f[2]; struct Matrix { int a[25][25]; }a,b,c; Matrix operator * (const Matrix &a,const Matrix &b) { memset(c.a,0,sizeof(c.a)); for (int i=0;i<tot;i++) { for (int j=0;j<tot;j++) { for (int k=0;k<tot;k++) c.a[i][j]=(c.a[i][j]+(LL)a.a[i][k]*b.a[k][j])%mo; } } return c; } int Get(int s,int w) { return (s>>(w<<1))&3; } void Set(int &s,int w,int v) { s^=Get(s,w)<<(w<<1); s^=v<<(w<<1); } void dp(int m) { p=0; q=1; f[0].init(); if (m>=0) f[0].add(m,1);else { for (int i=0;i<tot;i++) f[0].add(Sta[i],b.a[0][i]); } for (int i=0;i<n;i++,p^=1,q^=1) { f[q].init(); for (int j=0;j<f[p].tot;j++) { int st=f[p].st[j],s=f[p].s[j],x=Get(st,i),y=Get(st,i+1); if (!x) { if (!y) { if (i==n-1) continue; Set(st,i,1); Set(st,i+1,2); f[q].add(st,s); }else { if (i<n-1) f[q].add(st,s); Set(st,i,y); Set(st,i+1,0); f[q].add(st,s); } }else { if (!y) { f[q].add(st,s); if (i==n-1) continue; Set(st,i,0); Set(st,i+1,x); f[q].add(st,s); }else { Set(st,i,0); Set(st,i+1,0); if (x==1) { if (y==1) { for (int k=i+2,t=1;;k++) { if (Get(st,k)==1) t++; else if (Get(st,k)==2) { t--; if (!t) { Set(st,k,1); break; } } } f[q].add(st,s); }else if (m<0 && i==n-1) f[q].add(st,s); }else { if (y==1) f[q].add(st,s);else { for (int k=i-1,t=1;;k--) { if (Get(st,k)==2) t++; else if (Get(st,k)==1) { t--; if (!t) { Set(st,k,2); break; } } } f[q].add(st,s); } } } } } } for (int i=0;i<f[p].tot;i++) f[p].st[i]<<=2; } void Find(int x,int y,int st) { if (x>n) { if (!y) id[st]=tot,Sta[tot++]=st; return; } Find(x+1,y,st); Find(x+1,y+1,st+(1<<(x<<1))); if (y>0) Find(x+1,y-1,st+(2<<(x<<1))); } void quick(int x) { if (!x) return; quick(x>>1); b=b*b; if (x&1) b=b*a; } int main() { scanf("%d%d",&m,&n); memset(id,255,sizeof(id)); Find(1,0,0); for (int i=0;i<tot;i++) { dp(Sta[i]); for (int j=0;j<f[p].tot;j++) if (id[f[p].st[j]]>0) a.a[i][id[f[p].st[j]]]=f[p].s[j]; b.a[i][i]=1; } quick(m-1); dp(-1); for (int i=0;i<f[p].tot;i++) if (f[p].st[i]==0) { printf("%d\n",(f[p].s[i])*2%mo); return 0; } printf("0\n"); return 0; }
相关文章推荐
- 【51Nod 1142】棋子遍历棋盘 矩阵快速幂+插头DP
- flex 联机游戏开发 - 四国军棋游戏:(二)棋盘棋子
- 【弹子兵法】四国军棋棋盘、棋子与记谱【基础篇】
- [ACM_图论] 棋盘问题 (棋盘上放棋子的方案数)
- 中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制
- 马遍历棋盘
- 2D游戏之五子棋(1)显示棋盘,棋子
- [itint5]直角路线遍历棋盘
- 中国象棋游戏Chess(1) - 棋盘绘制以及棋子的绘制
- POJ 2993-Emag eht htiw Em Pleh(模拟-根据棋子位置还原棋盘)
- poj-1321棋盘问题(dfs 找出最多有几种摆放棋子的可能)
- 图像识别与处理之Opencv——识别直线与圆形(对应棋盘棋子)11月1日暂存
- 递归算法之马遍历棋盘问题
- 马的棋盘遍历
- 关于 马走棋盘 (骑士遍历)的深度优先算法
- 【转】韩寒:跳出棋盘的棋子
- 棋盘 围棋? 黑白棋子? 我忘记了
- 中国象棋棋子及棋盘的绘制
- 马踏棋盘(马的遍历问题)
- 图的遍历算法-马遍历棋盘