牛客——扫雷MINE(dp,dfs,枚举+递推)

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

相信大家都玩过扫雷的游戏。那是在一个n*m的矩阵里面有一些雷,要你根据一些信息找出雷来。

万圣节到了 ,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字 表示和它8连通的格子里面雷的数目。

现在棋盘是n×2的,第一列里面某些格子是雷,而第二列没有雷,如下图: 由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。

输入描述:

第一行为N,第二行有N个数,依次为第二列的格子中的数。(1 ≤ N ≤ 10000)

输出描述:

一个数,即第一列中雷的摆放方案数。

#include <iostream>
#include <cstdio>
using namespace std;

const int mxn = 10010;
int n;
int f[mxn][4], g[mxn];

int main() {
    int n;
    cin>>n;
    for (int i = 1; i <= n; ++i)
       cin>>g[i];

    if (g[1] == 0) f[1][0] = 1;
    else if (g[1] == 1) f[1][1] = f[1][2] = 1;
    else if (g[1] == 2) f[1][3] = 1;

    for (int i = 2; i < n; ++i) {
        if (g[i] == 0) f[i][0] = f[i-1][0];
        if (g[i] == 1) {
            f[i][0] += f[i-1][2];
            f[i][1] += f[i-1][0];
            f[i][2] += f[i-1][1];
        }
        if (g[i] == 2) {
            f[i][1] += f[i-1][2];
            f[i][2] += f[i-1][3];
            f[i][3] += f[i-1][1];
        }
        if (g[i] == 3)
            f[i][3] = f[i-1][3];
    }

    if (g[n] == 1)cout<< f[n-1][1] + f[n-1][2]<<endl;
    if (g[n] == 2) cout<<f[n-1][3]<<endl;
    if (g[n] == 3)cout<<'0'<<endl;
    if (g[n] == 0)cout<<f[n-1][0]<<endl;
    
    return 0;
}

以上的代码我搜了一下其他人写的,感觉好难想啊,这个是题解的链接:https://www.cnblogs.com/bbqub/p/8425718.html

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[10004];
int f[10004],n,i,j,ans,flag;
int hi(){
    for(int j=2;j<n;j++){//从2开始判断
        int t=a[j]-f[j-1]-f[j];//判断第i+1处是否有雷
        if(t<0 || t>1)  return 0;//不符合标准
        else    f[j+1]=t;
    }
    if(f[n]+f[n-1]!=a[n]){//特批一下最后两个
        return 0;
    }
    return 1;
}
int main()
{
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]>3) flag=1;
    }
    if(a[1]>2 || a[n]>2 || flag){
        printf("0\n");
        return 0;
    }
    if(a[1]==2){
        f[1]=1;
        f[2]=1;
        ans+=hi();
    }
    else if(a[1]==0){
        f[1]=0;
        ans+=hi();
    }
    else if(a[1]==1){
        f[1]=1;//地雷放第一个
        ans+=hi();
        memset(f,0,sizeof(f));
        f[2]=1;//地雷放第二个
        ans+=hi();
    }
    printf("%d",ans);
}

这个会容易理解些:

a[i]:第二列第i个数的值
f[i]:第一列的i个数是否有雷,1代表有,0代表无
f[i-1]+f[i]+f[i+1]=a[i]-->f[i+1]=a[i]-f[i-1]-f[i](扫雷规则)

先整体来看,显然对于每个a[i]的值均不能超过3,因为棋盘只有一列有雷,即以某点为中心最多只有3颗雷,特别的,第一个数和最后一个数不能超过2,因为有一个被挡住了。按顺序先从第一个值进行考虑,共有3种可能,0,1,2。
如果第一个数为0:第一列第1,2个位置可推出没有雷,通过f[i+1]=a[i]-f[i-1]-f[i]递推下去每个位置是否有雷为固定答案。
如果第一个数为2:第一列的第1,2个位置可推出均为雷,同上式可推出每个位置是否有雷为固定答案。
如果第一个数为1:那么则可能有2个答案,第一列的第1个位置有雷或者是第二个位置有雷,当其中一个位置有雷,即可推出另一个了,已知两个位置,用上面的递推式即可确定之后是否有雷了。

最后把其他格子的数枚举判断是否合法即可ac了。

#include<bits/stdc++.h>
using namespace std;
#define maxn 10004
int n;
int a[maxn],b[maxn];
int ans;
void dfs(int x)
{
    int sum=0;
    if(b[x-1]) sum++;
    if(b[x]) sum++;
    if(sum==a[x]-1)//下一个点必须是地雷
    {
        if(x+1 > n) return ; 
        b[x+1]=1;//有地雷
        dfs(x+1);
        b[x+1]=0;
    }
    if(sum!=a[x]) return;
    if(x==n)
    {
        ans++;
        return;
    }
    dfs(x+1);//没有地雷。
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    b[1]=1;dfs(1);
    memset(b,0,sizeof(b));dfs(1);
    printf("%d\n",ans);
    return 0;
}

上面的用dfs暴瘦一下,每个点只能是有或者没有地雷,按照这两个状态进行搜索即可。
每次枚举完记得判断一下是否为可行解,进行剪枝。
注意一下第一个点要分类讨论放不放地雷。

突然发现想复杂了其实,看了大佬的代码是这样的:

#include<iostream>
using namespace std;
int n,a[10005],f[10005],ans=0;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=0;i<=a[1];i++)
	{
		f[1]=i;
		for(int j=2;j<=n+1;j++)
		f[j]=a[j-1]-f[j-1]-f[j-2];
		if(f[n+1]==0) ans++;
	}
	cout<<ans;
}
#include<iostream>
using namespace std;
const int N=1e5+10;
int ans;
int a[N];
int n;
void dfs(int x,int lst,int now){
	if(x== n+1 && now==0 ) {ans++;return;}
	if(now + lst > a[x]) return ;
	dfs(x+1,now,a[x]-lst-now);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	dfs(1,0,1);
	dfs(1,0,0);
	cout<<ans<<"\n";
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,a[N],f[N],ans;
bool check(){
	for(int i=2;i<=n;i++){
		f[i]=a[i-1]-f[i-1]-f[i-2];
		if(f[i]<0||f[i]>1) return 0;
	}
	return a[n]==f[n]+f[n-1];
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	f[1]=1;if(check()) ans++;
	f[1]=0;if(check()) ans++;
	cout<<ans;
} 
#include<bits/stdc++.h>

using namespace std;

int n,a[2][10005];
int i;

bool b(bool k){

	a[1][1]=k;
	for(i=1;i<=n;i++){
		if(a[1][i]+a[1][i-1]>a[0][i]){
			return 0;
		}
		a[1][i+1]=a[0][i]-a[1][i-1]-a[1][i];
	}
	return !a[1][n+1];
}

int main()
{
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&a[0][i]);
	}
	printf("%d\n",b(0)+b(1));
	return 0;
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值