Description
升降梯的密码盘是一个由n*n个方格组成的正方形(n为偶数),第i行第j列的方格中标着数字(i-1)*n+j,而在密码盘的上面覆盖着一张同样由n*n个方格组成的挡光片。如果挖去挡光片上的n*n/4个格子,并从小到大记下通过挖去的格子看到的n*n/4个数;然后把挡光片顺时针旋转90°、180°、270°,每次也同样记下看到的n*n/4个数;这样最终将得到n*n个数。如果记下的这n*n个数恰好是1~n*n的一个排列,那么我们称这片挖去n*n/4个格子后的挡光片对密码盘是“精确覆盖”的。不妨用一个n*n的01矩阵表示这张挡光片,其中挖去的格子为1,没有挖去的格子为0,那么如下图所示的挡光片就可以表示为:
0000
0000
1101
0001
定义挡光片A比挡光片B小,当且仅当A对应的01矩阵的字典序小于B对应的01矩阵的字典序(即:A和B对应的01矩阵中存在一个位置(x,y),使得矩阵A、B中前x-1行的所有数字和第x行的前y-1个数字都相同,而A中第x行第y列的数字为0,B中第x行第y列的数字为1)。
现在升降梯口的墙上写着一个数字k,探险队员们必须迅速制作出第k小的、对密码盘“精确覆盖”的挡光片,用以在密码盘上获取n*n个数作为开启升降梯动力的密码。
Input
一行两个正整数n、k。
Output
输出满足要求的挡光片对应的01矩阵。
Sample Input
4 15
Sample Output
0000
0000
1101
0001
Hint
测试点编号 n k
#1 =2 <=10
#2 =2 <=10^3
#3 =4 <=10
#4 =4 <=10^9
#5 =6 <=10
#6 =6 <=10^18
#7 =8 <=10
#8 =8 <=10^18
#9 =10 <=10^18
#10 =10 <=10^18
数据保证有解。
【分析】
这道题思考起来真的很有难度。首先我们不难判断出可以将整个N×N的正方形分成四块,然后其中一块可以通过旋转得到整个图形。不妨以样例为例,若用ABCD对遮光片进行编号,我们可以得到这样一个图。
A | B | C | A |
C | D | D | B |
B | D | D | C |
A | C | B | A |
完成了这一步,剩下的就很简单了。我们现在需要确定的便是每一个1可以放在什么地方。很显然,既然要满足第k大, 那么当前位置可以填1的条件为:剩下所有格子的方案数(记为temp)>=K; 这样才能保证为第k大。如果不满足,则这一位填0。K-=temp。
最后扫完整个图,答案就求出来了。
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const long long INF=1000000000000000005LL;
int Map[15][15],f[500],N,tot;
//Map为每个位置的编号。同一编号的位置表示能旋转到达
//f的下标是编号,表示这个编号还有多少种可能的方案
bool ans[15][15],vis[500];
//ans记录答案
//vis的下标是编号,表示这个编号是否填过1(填过就不能再填了)
long long K;
long long _findc() //根据乘法原理求方案总数
{
long long sum=1;
for(int i=1;i<=tot;i++)
if(!vis[i]&&sum<INF)
sum*=f[i];
return sum;
}
void _init()
{
scanf("%d%I64d",&N,&K);
}
void _solve()
{
int x,y;
for(int i=1;i<=N;i++) //建图Map
for(int j=1;j<=N;j++)
if(!Map[i][j])
{
tot++;
f[tot]=4;
x=i;
y=j;
for(int k=0;k<4;k++)
{
swap(x,y);
x=N-x+1;
Map[x][y]=tot;
}
}
long long temp;
K=(1LL<<2*tot)+1-K; //这里要加上LL,c++默认常数是int类型
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(vis[Map[i][j]]==false)
{
vis[Map[i][j]]=true;
//在查询方案数前假设此位置已填1,标记
temp=_findc();
if(K<=temp)
ans[i][j]=true;
else
{
vis[Map[i][j]]=false; //若不能填1,去掉标记
f[Map[i][j]]--;
K-=temp;
}
}
for(int i=1;i<=N;i++) //输出答案
{
for(int j=1;j<=N;j++)
{
if(ans[i][j])
putchar('1');
else
putchar('0');
}
putchar('\n');
}
}
int main()
{
_init();
_solve();
return 0;
}