外星密码

【问题描述】
小 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值