外星密码
【问题描述】
小 W 潜入了外星人的基地刺探情报,遇到一个二级密码系统。一级密码是一个长度为
n 的 0-1 序列 B, 记为(b1 b2 ⋯ bn)。 将一级密码的第一位放到最后,得到一个新的序
列(b2 b3 ⋯ b1), 继续做同样的操作得到(b3 b4 ⋯ b2),如此反复,总共可以得到
n 个序列,将这些序列按字典序排序后,字典序最小的即为二级密码,输入这个二级密
码,就可以得到情报。
外星人将领将排序过后的 n 个字符串按照字典序从小到大的顺序逐行写成了一个 n 阶
矩阵,而且小 W 恰好看到了这个矩阵,不难发现,这个矩阵的第一行就是二级密码,所以
如果小 W 记住这个矩阵的第一行,就可以直接获得情报。但是小 W 在出发前听上司布置
任务时走神将“记住第一行”听成了“记住最后一列”。所以小 W 现在只记得最后一列是
什么,小 W 赶紧联系远在地球的你,希望你能帮他由矩阵的最后一列得到矩阵的第一行。
【输入格式】
共两行,第一行一个整数 n,第二行 n 个值为 0 或 1 的整数,表示矩阵的最后一列。
【输出格式】
输出文件共一行,为矩阵的第一行。
【样例】
5
1 1 0 0 0
0 0 1 0 1
【数据规模与约定】
对于 20%的测试数据n ≤ 20
对于 50%的测试数据 n≤ 1000
对于 100%的测试数据n ≤ 10000
【样例解释】
一级密码为(0 1 0 0 1),因此得到 5 个序列为:
( 0 1 0 0 1
1 0 0 1 0
0 0 1 0 1
0 1 0 1 0
1 0 1 0 0 ) , 经过排序过后的矩阵为:
( 0 0 1 0 1
0 1 0 0 1
0 1 0 1 0
1 0 0 1 0
1 0 1 0 0 ) ,
第一行为(0 0 1 0 1)。
这道题我当时是没有完成的,我只完成了暴力20分。
正解是极其难以想到,反正我是没想到。
首先可以观察发现,最左边上面一定全是0,下面一定有与最后一列的1的个数相同个数的1
如下可见第一列与最后一列的关系(样例中的)
0 1
0 1
0 0
1 0
1 0
先假设第一行为
0 a b c 1
则左移之后变为
a b c 1 0
不难发现,此时末尾为0,证明过程略(不会),粗略的理解一下
举个例子:
0 1 0 1 1 1 0 1 1 1 0 1 1 0 1 0 1 1 1 0 1 1 1 0 1 1 0 1 0 1 1 1 1 0 1 0
第一行移动后 : 1 0 1 1 1 0
第二行移动后: 1 1 1 0 1 0
那么必然的,排序后第一行移动的0在第二行的前面(在最后一列的顺序中)。
故可以粗略地得出,第一列与最后一列中的对应规则:
第一列的第n个0对应最后一列的第n个0
第一列的第n个1对应最后一列的第n个1
由此,来依次确定a、b、c的值。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int a[10005],n,Tot,Done,Key[2][10005];
void Find(int Aim,int Num) {
if(Done == n){
return;
}
if(Done) {
printf(" ");
}
Done++;
printf("%d",Aim);
if(Key[Aim][Num] <= n - Tot) {
Find(0,Key[Aim][Num]);
} else {
Find(1,Tot - (n - Key[Aim][Num]));
}
}
int main() {
scanf("%d",&n);
for(int i = 1; i<=n; i++) {
scanf("%d",&a[i]);
Tot += a[i];
if(a[i]) {
Key[1][Tot] = i;
} else {
Key[0][i - Tot] = i;
}
}
if(Tot == n) {
for(int i = 1; i<=n; i++) {
if(i != 1) {
printf(" ");
}
printf("1");
}
return 0;
}
Find(0,1);
return 0;
}