[DFS/递推/DP] 2327 [SCOI2005] 扫雷 ( 普及+/提高

1 篇文章 0 订阅

Date:2019/10/13
Degree of difficulty:Universal
Original question:P2327 [SCOI2005]扫雷

在这里插入图片描述

  • 原题与改编

在这里插入图片描述
10.13月考【爆零祭】
T103466 新扫雷游戏

怎么说呢,作为一个没有经历过多少比赛的蒟蒻,今天的考试还是有点虚的,毕竟没有多少做题经验,有些步骤还是有些生疏,做题时候也有些慌乱,没有把握好节奏。
再有一点,就是生活经验不是很多,连扫雷都没玩过,一个坑竟然不能放3个雷??
所以要把这种有关游戏的题目和中国传统的游戏方式(与现在学习知识有关的)多看看看

杨老师说:与其蜻蜓点水,每个题20、30分,还不如好好的沉下心来,AC一道题

目录

  1. 本题思路
  2. 数组递推
  3. 深搜
  4. 一些学会的东西

Ox00 本题思路

这道题我们很快的就能知道
右边一列的每一个数是等于左边相邻的3列的和的
所以得到公式b[i]=a[i]+a[i-1]+a[i+1];
但是我们现在已知的东西是右边的数,左边的的前两列,所以问题就变成了求第三列的数
移项易得a[i]=b[i]-a[i-1]-a[i+2];
所以用这个递推公式

但注意!!!
每个格子只能放1~2个雷 (没玩过扫雷哭晕)
所以第一个格子只有三种情况

1. 有0个雷,那么第一列第一行第二行都是0;0+0=0
2. 有一个雷
2.1 第一列第一行是1,第二行是0;1+0=1
2.2 第一列第一行是0,第二行1;0+1=1
3. 有两个雷,那么第一列第一行第二行都是1;1+1=2

所以每次加减以后都要判断它是不是1或者2,成功剪枝;

还要在最后一个点的时候判断a[n-1][1]+a[n][1]==a[n][2]
如下

for(int i = 3; i <= n; i++){//dfs程序 
		a[i][1] = a[i-1][2] - a[i-1][1] - a[i-2][1];
		if(a[i][1] > 1 || a[i][1] < 0){
			fl = 1 ; break;
		}
	}
	if(fl==0&&a[n-1][1]+a[n][1]==a[n][2])
		vie++;

Ox01 深搜

dfs的思路就是每个格子只能放0个或者1个
所以搜索就好
但是我这个剪枝没有剪好
所以先不放代码了
(未完待续)

Ox02 DP

这道题也可用DP做
详情请见
这道题根据我的老师的思路是定义一个三维数组dp[i][j][k]来做动态规划
其中j表示第i行的地雷数,k表示第i+1行的地雷数,
而dp[i][j][k]代表当前的组数。
初始条件为dp[0][0][0]=dp[0][0][1]=1
即第1行为0个或为1个时都有一种可能。然后根据第i行给的ai来进行动态转移。

  1. 若ai==0,则i-1,i,i+1必须分别对应 0,0,0,
    所以这种情况下转移方程: dp[i][0][0]=dp[i-1][0][0]
  2. 若ai==3,则i-1,i,i+1必须分别对应 1,1,1
    所以这种情况下转移方程 dp[i][1][1]=dp[i-1][1][1]
  3. 若ai==1,则分为三种情况
    i-1 i i+1 动转方程

1 0 0 dp[i][0][0]=dp[i-1][1][0]

0 1 0 dp[i][1][0]=dp[i-1][0][1]

0 0 1 dp[i][0][1]=dp[i-1][0][0]

  1. 若ai==2,也是三种情况

i-1 i i+1 动转方程

1 1 0 dp[i][1][0]=dp[i-1][1][1]

0 1 1 dp[i][1][1]=dp[i-1][0][1]

1 0 1 dp[i][0][1]=dp[i-1][1][0]

记得开全局变量因为这样不合法的转移就全部是0了。 在最后输出的答案为dp[n][1][0]+dp[n][0][0],这是第n行有地雷或无地雷的数量的总和(n+1不能取)。 大概就是这样详情请见代码,我觉得挺简洁的。

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
int n,a[10005];
int dp[10005][2][2];
int main(){
    cin>>n;
    for (int i=1;i<=n;i++) cin>>a[i];//读入数据
    dp[0][0][0]=dp[0][0][1]=1;//初始化
    for (int i=1;i<=n;i++){//状态转移
        if (a[i]==0){
            dp[i][0][0]=dp[i-1][0][0];
        }
        if (a[i]==3){
            dp[i][1][1]=dp[i-1][1][1];
        }
        if (a[i]==2){
            dp[i][1][1]=dp[i-1][0][1];
            dp[i][1][0]=dp[i-1][1][1];
            dp[i][0][1]=dp[i-1][1][0];
         }
         if (a[i]==1){
            dp[i][0][1]=dp[i-1][0][0];
            dp[i][1][0]=dp[i-1][0][1];
            dp[i][0][0]=dp[i-1][1][0];
         }
    }
    cout<<dp[n][1][0]+dp[n][0][0];
}

//作者: momo5440 更新时间: 2019-03-20 23:37

OxFF 学到的一些东西

  1. 输入输出的写法
	freopen("XX(文件名).in","r",stdin);//r is read
	freopen("XX(文件名).out","w",stdout);// w is write

看注释会记得牢一点
2. 扫雷一个格子放0或者1个
3. 数组不清零,爆零两行泪

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值