在我们做题中,搜索也好,动态规划也好,我们往往有时候需要用一个数字表示一种状态

比如有8个灯泡排成一排,如果你用0和1表示灯泡的发光情况

那么一排灯泡就可以转换为一个二进制数字了

比如

01100110 = 102

11110000 = 240

10101010 = 170

通过这些十进制数,只要把他们展开,我们就知道灯泡的状态了

如果这题是一个动态规划题

然后我们就拿这些数字做一些转移了,

比如dp[102],dp[240],dp[170]等等

这对题目很有帮助

上面讲的那些就是所谓的状态压缩了,须知详细的状态压缩可以去百度

或者有机会我自己去写一篇博客(这是flag(/TДT)/)

那对于有些题,我们即使状态压缩后,数字太大,数组都开不下,麻烦的题目(/TДT)/

这些题目也要看情况,比如我接下来要讲的康托展开

康托展开经典题:hdu 1430

http://acm.hdu.edu.cn/showproblem.php?pid=1430

在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板。魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示。任一时刻魔板的状态可用方块的颜色序列表示:从魔板的左上角开始,按顺时针方向依次写下各方块的颜色代号,所得到的数字序列即可表示此时魔板的状态。例如,序列(1,2,3,4,5,6,7,8)表示魔板状态为:

1 2 3 4
8 7 6 5

对于魔板,可施加三种不同的操作,具体操作方法如下:

A: 上下两行互换,如上图可变换为状态87654321
B: 每行同时循环右移一格,如上图可变换为41236785
C: 中间4个方块顺时针旋转一格,如上图可变换为17245368

给你魔板的初始状态与目标状态,请给出由初态到目态变换数最少的变换步骤,若有多种变换方案则取字典序最小的那种。

Input
每组测试数据包括两行,分别代表魔板的初态与目态。
 
Output
对每组测试数据输出满足题意的变换步骤。
 
Sample Input
12345678
17245368
12345678
82754631
 
Sample Output
C
AC

我们看这题,总共有8个数字,1~8,假如我们把他们看成0~7

那么每个数字可以转换为一个3位二进制

0:000

1:001

2:010

3:011

4:100

5:101

6:110

7:111

然后12345678这个状态我们可以表示为二进制000001010011100101110111,总共3*8=24位,

2^24 = 16777216,数组根本开不下啊

这时,我们发现了,有一些状态,根本没有用到,因为这题已经规定了有8个数字,每个数字只出现一次

比如000000000000000000000000这个状态,你说可能出现吗?(o ° ω ° O )

这个时候,康托就对这种题目做了研究(o ° ω ° O )

这种每个数字只出现一次的问题的所以情况,总共才n!个情况(这个问题叫做全排列)

康托的一套算法可以正好产生n!个数字

比如:

123  ->  0

132  ->  1

213  ->  2

231  ->  3

312  ->  4

321  ->  5

这是如何做到的(/≥▽≤/)

在峰神的博客里面有很好的解释(对不起了峰神≖‿≖✧,拿过来抄一下)

(/≥▽≤/)好神奇

于是乎,康托展开模板:

 void cantor(int s[], LL num, int k){//康托展开,把一个数字num展开成一个数组s,k是数组长度
int t;
bool h[k];//0到k-1,表示是否出现过
memset(h, , sizeof(h));
for(int i = ; i < k; i ++){
t = num / fac[k-i-];
num = num % fac[k-i-];
for(int j = , pos = ; ; j ++, pos ++){
if(h[pos]) j --;
if(j == t){
h[pos] = true;
s[i] = pos + ;
break;
}
}
}
}
void inv_cantor(int s[], LL &num, int k){//康托逆展开,把一个数组s换算成一个数字num
int cnt;
num = ;
for(int i = ; i < k; i ++){
cnt = ;
for(int j = i + ; j < k; j ++){
if(s[i] > s[j]) cnt ++;//判断几个数小于它
}
num += fac[k-i-] * cnt;
}
}

付上AC代码:

(这代码我在杭电上用c++交竟然CE了,g++就没问题,CE的内容是我的那个模板,说什么不能bool h[k]这样声明类型,c++小心眼,这有什么关系嘛(´・ω・)ノ,我还只是个孩子)

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int N = ;
queue <LL> que;
string ans[];
char str1[], str2[];
bool vis[]; int map[];//映射
int num[]; LL fac[N];//阶乘
void change(int s[], int o){//o分别是0,1,2,表示ABC三种变化
switch(o){
case :
for(int i = ; i < ; i ++) swap(s[i], s[-i-]);
break;
case :
for(int i = ; i >= ; i --) swap(s[i], s[i-]);
for(int i = ; i < ; i ++) swap(s[i], s[i+]);
break;
case :
swap(s[], s[]);
swap(s[], s[]);
swap(s[], s[]);
break;
}
}
void cantor(int s[], LL num, int k){//康托展开,把一个数字num展开成一个数组s,k是数组长度
int t;
bool h[k];//0到k-1,表示是否出现过
memset(h, , sizeof(h));
for(int i = ; i < k; i ++){
t = num / fac[k-i-];
num = num % fac[k-i-];
for(int j = , pos = ; ; j ++, pos ++){
if(h[pos]) j --;
if(j == t){
h[pos] = true;
s[i] = pos + ;
break;
}
}
}
}
void inv_cantor(int s[], LL &num, int k){//康托逆展开,把一个数组s换算成一个数字num
int cnt;
num = ;
for(int i = ; i < k; i ++){
cnt = ;
for(int j = i + ; j < k; j ++){
if(s[i] > s[j]) cnt ++;//判断几个数小于它
}
num += fac[k-i-] * cnt;
}
}
void init(){
fac[] = ;
for(int i = ; i < N; i ++) fac[i] = fac[i-] * i;
int a[], b[];
LL temp, temp2;
que.push();
vis[] = true;
while(!que.empty()){
LL temp = que.front(); que.pop();
cantor(a, temp, );
for(int i = ; i < ; i ++){
copy(a, a+, b);
change(b, i);
inv_cantor(b, temp2, );
if(!vis[temp2]){
que.push(temp2);
vis[temp2] = true;
ans[temp2] = ans[temp] + (char)('A' + i);
}
}
}
}
int main(){
init();
while(~scanf("%s", str1)){
scanf("%s", str2);
//先把所有初始状态都转换成12345678
//最终状态根据初始状态的转换而转换
//这样只要一次预处理就可以解决问题了
for(int i = ; i < ; i ++) map[str1[i] - ''] = i + ;
for(int i = ; i < ; i ++) num[i] = map[str2[i] - ''];
LL temp;
inv_cantor(num, temp, );
cout << ans[temp] << endl;
}
}

宇宙我来啦~\(≧▽≦)/~

ACM数论之旅12---康托展开((*゚▽゚*)装甲展开,主推进器启动,倒计时3,2,1......)的更多相关文章

  1. acm数论之旅--中国剩余定理

    ACM数论之旅9---中国剩余定理(CRT)(壮哉我大中华╰(*°▽°*)╯)   中国剩余定理,又名孙子定理o(*≧▽≦)ツ 能求解什么问题呢? 问题: 一堆物品 3个3个分剩2个 5个5个分剩3个 ...

  2. acm数论之旅--欧拉函数的证明

    随笔 - 20  文章 - 0  评论 - 73 ACM数论之旅7---欧拉函数的证明及代码实现(我会证明都是骗人的╮( ̄▽ ̄)╭) https://blog.csdn.net/chen_ze_hua ...

  3. acm数论之旅--组合数(转载)

    随笔 - 20  文章 - 0  评论 - 73 ACM数论之旅8---组合数(组合大法好(,,• ₃ •,,) )  补充:全错排公式:https://blog.csdn.net/Carey_Lu/ ...

  4. acm数论之旅(转载) -- 逆元

    ACM数论之旅6---数论倒数,又称逆元(我整个人都倒了( ̄﹏ ̄))   数论倒数,又称逆元(因为我说习惯逆元了,下面我都说逆元) 数论中的倒数是有特别的意义滴 你以为a的倒数在数论中还是1/a吗 ( ...

  5. acm数论之旅--数论四大定理

    ACM数论之旅5---数论四大定理(你怕不怕(☆゚∀゚)老实告诉我)   (本篇无证明,想要证明的去找度娘)o(*≧▽≦)ツ ----------数论四大定理--------- 数论四大定理: 1.威 ...

  6. ACM数论之旅16---母函数(又名生成函数)(痛并快乐着(╭ ̄3 ̄)╭)

    (前排出售零食瓜子) 前言: 母函数是个很难的东西,难在数学 而ACM中所用的母函数只是母函数的基础 应该说除了不好理解外,其他都是非常简单的 母函数即生成函数,是组合数学中尤其是计数方面的一个重要理 ...

  7. ACM数论之旅11---浅谈指数与对数(长篇)(今天休息,不学太难的数论> 3<)

    c/c++语言中,关于指数,对数的函数我也就知道那么多 exp(),pow(),sqrt(),log(),log10(), exp(x)就是计算e的x次方,sqrt(x)就是对x开根号 pow()函数 ...

  8. ACM数论之旅9---中国剩余定理(CRT)(壮哉我大中华╰(*°▽°*)╯)

    中国剩余定理,又名孙子定理o(*≧▽≦)ツ 能求解什么问题呢? 问题: 一堆物品 3个3个分剩2个 5个5个分剩3个 7个7个分剩2个 问这个物品有多少个 解这题,我们需要构造一个答案 我们需要构造这 ...

  9. ACM数论之旅13---容斥原理(一切都是命运石之门的选择(=゚ω゚)ノ)

    容斥原理我初中就听老师说过了,不知道你们有没有听过(/≧▽≦)/ 百度百科说: 在计数时,必须注意没有重复,没有遗漏. 为了使重叠部分不被重复计算,人们研究出一种新的计数方法. 这种方法的基本思想是: ...

随机推荐

  1. 运行tomcat7w.exe未安装指定的服务

    新安装了tomcat7.042,但是启动tomcat7w.exe是提示未安装指定服务,解决方法如下: 在运行窗口输入cmd,运行DOS,输入以下命令: cd E:\apache-tomcat-7.0. ...

  2. jenkins执行远程脚本注意的问题

    1构建任务 ->新建任务 2.填写任务名称    第二次构建可以复制第一次的配置 3.选择任务名称上面右键  选择配置 4.如下图所示 附录脚本内容 test.sh#!/bin/bash JAR ...

  3. 分布式计算(三)Azkaban介绍

    转载自:Azkaban学习之路 (一)Azkaban的基础介绍 目录 一.为什么需要工作流调度器 二.工作流调度实现方式 三.常见工作流调度系统 四.各种调度工具对比 五.Azkaban 与 Oozi ...

  4. 走近SpringBoot

    (博客园不支持MarkDown编辑,看完整版请移步:https://www.zybuluo.com/Allen-llh/note/1199946) 1. (Building a RESTful Web ...

  5. npm install xxx --save-dev 与npm install xxx --save 的区别

    正常情况下: 当你为你的模块安装一个依赖模块时 1.你得先安装他们(在模块根目录下npm install module-name) 2.连同版本号手动将他们添加到模块配置文件package.json中 ...

  6. Luogu P2286 [HNOI2004]宠物收养场

    一道比较简单的直接Treap运用题目,思维难度和代码难度都不是很高. 题意有点长,我们仔细剖析一下题意发现以下几个关键: 任何时候收养站里只可能有人和宠物中的其中一种,或者都没有 如果只有宠物并有人来 ...

  7. SPI内容随笔

    关于SPI的通信: SPI采用的是主从模式的同步通信,通过时钟来控制:一般情况下,使用双向全双工,收发的数据放在缓冲器FIFO中.数据的传输是主SPI的时钟在控制,从机是不能产生时钟的,如果没有时钟, ...

  8. 非关系型数据库(nosql)介绍

    非关系型数据库也叫Nosql数据库,全称是not noly sql. 2009年初,Johan Oskarsson举办了一场关于开源分布式数据库的讨论,Eric Evans在这次讨论中提出了NoSQL ...

  9. Shell编程基础篇-上

    1.1 前言 1.1.1 为什么学Shell Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚 ...

  10. Matlab入门笔记(1)

    1.简单练习题: cos(((1+2+3+4+5)^3/5)^0.5) sin(pi^0.5)+log(tan(1)) 2^(3.5*1.7) exp(sin(10)) 2.实数,复数,行向量,列向量 ...