状态压缩模版3:地雷
首先这个应该可以说是我短时间内的这个博客,也就是说很长的一段时间应该都不会再去写博客了,当然有喜欢的东西我还是要记录下来,所以不多说,看一下这道题
1422: [视频]【状态压缩】地雷
时间限制: 1 Sec 内存限制: 4 MB
提交: 188 解决: 111
[提交] [状态] [讨论版] [命题人:admin]题目描述
题意
相信大家都玩过扫雷的游戏。那是在一个n*n的矩阵里面有一些雷,要你根据一些信息找出雷来。万圣节到了,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字表示和他8连通的格子里面雷的数目。现在棋盘是n*2的,第一列里某些格子是雷,而第二列没有雷,如:
o 1
* 2
* 3
* 2
o 2
* 2
* 2 ('*'代表有雷,'o'代表无雷)
由于第一类的雷有可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息求第一列雷有多少中摆放方案。
输入
第一行为N,第二行有N个数,依次为第二列的格子中的数。(1<=N<=10000)
输出
一个数,即第一列中雷的摆放方案数。
样例输入
2
1 1
样例输出
2
我相信标题已经义正言辞的告诉我们要用状态压缩但是怎么用呢,包括的话这道诡异的题目他问我们的到底是什么呢,下面看讲解
首先这个棋盘是2*n的,玩法和我们一样,但是他的规模却远远小于我们的标准扫雷,所以的话我说一个点的周围最大只有3个雷是完全可以理解的,因为上下是数字不是雷,然后右边是空的,也就是只有左边有3个雷,所以题目就是这个意思
然后的话怎么用这个压缩是我们一定要考虑的问题,我们知道一共有3行(意思就是说最多有三个雷),那么如果我们定义一个f状态,就是判断这个点的当前行有雷和下一行有雷,那么假如说
000 这个是我们的雷的布阵的话,那么我们是不是设当前的为中间的0,下一行就为第三个0,那么我们继承的上一个状态的当前行就是第一个0,下一行就是第二个0,那么我们就可以去判断到底有多少种情况了,是吗?,所以接下来我们来看一下情况的分布情况
简要分析一下这个继承状态是怎么样实现的啊
首先我们知道的就是说f[k][2][2]表示的就是第k行的状态的处理
那么第一个参数2表示的就是说下一行有没有雷的情况啊,1表示有雷,0表示没有雷
那么第二个参数2表示的就是说当前行有没有雷的情况啊,1表示有雷,0表示没有雷
那么就会出现4种情况,1没有雷 2有一个雷 3有两个雷 4有三个雷
(备注:红色表示的是我们当前要处理的状态,蓝色表示我们上一行的状态)
第一种情况:只有一个继承的状态
0 0 0,这个是我们的情况的显示,不难看出当前行和下一行都没有雷,然后上一个状态也是当前行和下一行都没有雷
第二种情况:有三个继承的状态
0 1 0,当前行有雷,上一个状态的当前行没有雷,下一行有雷
0 0 1,下一行有雷,上一个状态的当前行和下一行都没有雷
1 0 0,当前行和下一行都没有雷,上一个状态的当前行有雷,下一行没有雷
第三种情况:同样有三个继承的状态
0 1 1,当前行和下一行都有雷,上一个状态的当前行没有雷,下一行有雷
1 1 0,当前行有雷,上一个状态的当前行和下一行都有雷
1 0 1,下一行有雷,上一个状态的当前行有雷,下一行没有雷
第四种情况,只有一个继承的状态
1 1 1,当前行有雷和下一行有雷,继承上一个状态的当前行有雷和下一行有雷
所以看到这四种情况之后我相信基本上代码的模版已经有感觉了,所以的话我们的最后答案就是处理完全部之后,最后一行有雷和最后一行没有雷的情况的数量的总和就是我们的总数量
代码的实现(我直接放一个注释版的,其实讲的也很清楚了,而且码量很小,记忆起来也比较方便)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 int a[10010]; 9 int f[10010][2][2];/*第二个参数1表示下一行还需要一个1,0表示下一行不需要1, 10 第三个参数表示的是当前行有没有雷,1是有雷,2是没有雷*/ 11 int main() 12 { 13 int n; scanf("%d",&n); 14 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 15 memset(f,0,sizeof(f)); f[0][0][0]=1; f[0][1][0]=1;/*0和1的情况都初始化*/ 16 for(int i=1;i<=n;i++) 17 { 18 if(a[i]==0) f[i][0][0]=f[i-1][0][0];/*没有雷,就是说i-1,i+1都没有雷,就可以直接继承上一个状态*/ 19 else if(a[i]==1)/*有一个雷*/ 20 { 21 f[i][0][1]=f[i-1][1][0];/*当前行有雷,所以上一个状态的当前行无雷,下一行有雷*/ 22 f[i][1][0]=f[i-1][0][0];/*下一行有雷,所以上一个状态的当前行和下一行都应该没有雷*/ 23 f[i][0][0]=f[i-1][0][1];/*当前行和下一行都没有雷,则上一个状态的当前行有雷,下一行无雷*/ 24 } 25 else if(a[i]==2)/*有两个雷*/ 26 { 27 f[i][0][1]=f[i-1][1][1];/*当前行有雷,所以上一个状态的当前行和下一行都有雷*/ 28 f[i][1][0]=f[i-1][0][1];/*下一行有雷,所以上一个状态的当前行有雷,下一行没雷*/ 29 f[i][1][1]=f[i-1][1][0];/*当前行和下一行都没有雷,则上一个状态的下一行有雷,当前行没有雷*/ 30 } 31 else if(a[i]==3) f[i][1][1]=f[i-1][1][1];/*有三个雷,所以上一个状态也是同样的情况,当前行和下一行都有雷*/ 32 } 33 printf("%d\n",f[n][0][0]+f[n][0][1]);/*最后就分成最后一行有雷和无雷的情况相加*/ 34 return 0; 35 }
所以要很久以后再会了,毕竟我要进击算法进阶了,所以的话如果还会出现估计就是提到一些比较有意思或者说困扰了我很久的题了