在做题的时候,内存总是限制着我们.如果有一些数据的状态需要你标记或者记录,数据很小都好解决,直接记录就行,但是如果数据过大,内存就会超限,又如何解决呢?这个时候就可以采用状态压缩.
状态压缩并不是指某种算法,它是指一种思想,将很大的空间进行一种压缩,但是又可以保证原本需要记录的东西被记录下来的思想,现实生活中的二维码在我看来就是一种状态压缩,将信息都压缩在一张黑白格子图上,在二维码被扫描的时候进行解码复原,就会获得原本要储存的信息,大大节省了储存空间.在各种状态压缩的方式中,个人感觉二进制压缩法是最为省空间和便捷的.在下面先讲一下蓝桥算法训练的和为T,了解二进制压缩法是怎样把状态进行压缩的.
题目连接:http://lx.lanqiao.cn/problem.page?gpid=T290
试题 算法训练 和为T
问题描述
从一个大小为n的整数集中选取一些元素,使得它们的和等于给定的值T。每个元素限选一次,不能一个都不选。
输入格式
第一行一个正整数n,表示整数集内元素的个数。
第二行n个整数,用空格隔开。
第三行一个整数T,表示要达到的和。
输出格式
输出有若干行,每行输出一组解,即所选取的数字,按照输入中的顺序排列。
若有多组解,优先输出不包含第n个整数的;若都包含或都不包含,优先输出不包含第n-1个整数的,依次类推。
最后一行输出总方案数。
样例输入
5
-7 -3 -2 5 9
0
样例输出
-3 -2 5
-7 -2 9
2
这个题一开始我是用dfs做的,简单的拼数问题,dfs模板,但是我忽略了输出顺序,导致只得了80分.后来采用了二进制压缩的方法,把每种取数的情况列举才ac.先上代码,在详细讲解思路:
#include<iostream>
#include<cmath>
using namespace std;
int a[23],vis[23],summ=0;
void print(int idx){
for(int i=0;i<idx;i++)cout<<vis[i]<<" ";
cout<<endl;
summ++;
return;
}
int main(){
int n,m,t;
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
cin>>m;
for(int i=1;i<pow(2,n);i++){
t=i;
int idx=0,sum=0,mm=0;
while(mm!=n){
if(t%2==1){
vis[idx]=a[mm];
idx++;
sum+=a[mm];
}
t/=2;
mm++;
}
if(sum==m)print(idx);
}
cout<<summ;
}
二进制压缩:
就拿样例讲,一共有五种数字,我们可以拿一个五位的二进制数来表示他们的拿取状态,1表示当前位子已经取过,0表示没被拿.从右往走,分别是1,2,3,4....什么都不拿的情况就是00000,拿第一个就是00001,拿第五个和第三个就是10100,拿第二个第四个就可以记录为01010.这样就用了一个五位二进制数,最大是63,记录下了所有的63种情况.再用一个for循环从1循环到63,将每种情况列举进行计算,如果满足条件就输出.而且因为是从00001开始,刚好满足题目中的输出条件.这就是二进制压缩的思想,有了思想就可以看下一个题了.
链接:https://ac.nowcoder.com/acm/contest/16910/D
来源:牛客网
题目描述
今天是阳光明媚,晴空万里的一天,CSL早早就高兴地起床走出寝室到校园里转悠。
但是,等到他回来的时候,发现他的校园卡不见了,于是他需要走遍校园寻找它的校园卡。CSL想要尽快地找回他掉的校园卡,于是便求助于OneDay帮他一起找。
OneDay和CSL在同一已知的地点出发,并以相同的速度(1格/秒)搜索校园,试求两人走遍校园的最短时间。
输入描述:
第一行为两个整数n,m(1 ≤ n, m ≤ 4),表示地图的大小。接下来是n行m列的地图:X表示障碍物,S表示起点,O表示空地。障碍物不能直接经过,数据保证所有空地是可达的,起点有且只有一个。
输出描述:
输出一个整数表示两人共同走遍校园所需的最少时间。
示例1
输入
3 4
XSOO
OOXO
OOOO
输出
5 5
说明
输入
2 3
XSX
OOO
输出
2
输入
4 4
SOOO
OOOO
OOOO
OOOO
输出
8
看见这个题第一眼就会想到bfs,但是细看又好像不是,该如何记录他们走的状态呢?两个人走重复的路又怎么标记呢?这个时候再去回过头看看之前的状态压缩:
我们可以把地图变为一个二进制数来进行储存:
当地图为
OOSO
XXOO
OOOO
时我们把当前状态进行储存,记录为
0010
1100
0000
有感觉了吗?那么两人分别往左往下走一步走一步就可以记录为:
0110
1110
0000
这样就把每种状态记录了,记录了每种状态,就可以防止重复了!但是要注意,还要同步记录此时两人的位置,防止重复.当你把这个状态记录后,那它就变成了一个bfs问题了(bfs在我的博客中详细见:https://mp-new.csdn.net/mp_blog/creation/editor/116354137),定义四个方向去搜索,遍历图直到到达一种状态,两人都走满,也就是1111 1111 1111的状态.
针对这个题还有一个问题,如何判断下一步可以不可以走呢?我们可以保留原来的地图数组,如果bfs下一步原来是'O'或者'S'就可以走,'X'就不可以走.
状态压缩重在将状态压缩后进行空间的节省,方法不止二进制一种,只要能将每种情况用一个数字或者别的什么保存,进行空间压缩,就可以称之为状态压缩.