当前位置: 首页 > article >正文

[洛谷]P1123 取数游戏

最近准备蓝桥杯 一直在练搜索和图论hhh

题意

给定 n × m n \times m n×m的数字矩阵
可以取出若干数字
但是有限制 取出的两个数字不能在八联通方向上相邻


数据范围

1 ≤ N , M ≤ 6 , 1 ≤ T ≤ 20 1≤N,M≤6,1≤T≤20 1N,M61T20

思路

遍历方式

这道题的dfs方式对我来说是第一次接触
学到很多

本题采取遍历矩阵中的行优先的方式
可以理解为把下面这段常见的双重循环遍历拆成dfs中的坐标一步一步加减

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)

也就是对于每一行(每一个 x x x)
先把这一行的每一列走完 (先 y y y++)
然后再把 x x x++

如何保证八联通内不相邻?

定义一个别样的 v i s vis vis数组 i n t int int 类型
用来记录 ( i , j ) (i,j) (i,j)这个点八联通范围内有多少个点被选过


如果vis[i][j]==0 对于这个点可以有选和不选两种操作

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define pii pair<int,int>
#define ar2 array<int,2>
#define ar3 array<int,3>
#define ar4 array<int,4>
#define endl '\n'
void cmax(int &a,int b){a=max(a,b);}
void cmin(int &a,int b){a=min(a,b);}
const int N=20,MOD=1e9+7,INF=0x3f3f3f3f,LINF=LLONG_MAX;
int dx[]={1,1,1,0,0,-1,-1,-1};
int dy[]={1,-1,0,1,-1,0,1,-1};

int n,m,g[N][N];
int vis[N][N];
int ans=0;

void dfs(int x,int y,int sum){
    if(y==m+1){
        cmax(ans,sum);
        dfs(x+1,1,sum);
        return;
    }
    if(x==n+1){
        cmax(ans,sum);
        return;
    }
    
    //不选
    dfs(x,y+1,sum);
    
    if(vis[x][y]==0) {
            
        //选
        // vis[x][y]=1;
        for(int i=0;i<8;i++){
            int tx=x+dx[i],ty=y+dy[i];
            if(tx<1||tx>n||ty<1||ty>m) continue;
            vis[tx][ty]++;
        }

        dfs(x,y+1,sum+g[x][y]);
        
        // vis[x][y]=0;
        for(int i=0;i<8;i++){
            int tx=x+dx[i],ty=y+dy[i];
            if(tx<1||tx>n||ty<1||ty>m) continue;
            vis[tx][ty]--;
        }
    }
}

void solve() {
    memset(vis,0,sizeof vis);
    memset(g,0,sizeof g);
    ans=0;

    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
        }
    }
    
    dfs(1,1,0);
    cout<<ans<<endl;

}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t=1;
    cin>>t;
    while (t--) solve();

}

代码细节

  • dfs中 在最前面判断完x和y之后
    不要单独判断vis[x][y]==1
if(vis[x][y]){
	dfs(x,y+1,sum);
	return;
}

要判断的话就要写成这样 里面会多一次dfs
然后在这个判断过后 再分类讨论选或不选
这样会多很多次递归 会tle

  • 所以要写成上面code这样
  • 先走不选这个dfs 因为无论这个点能不能选 不选这个选项肯定要走一次的
  • 然后也不要判断vis[x][y]!=0 因为会多一次dfs
  • 直接判断vis[x][y]==0 然后 后面写选这个点的情况
	//不选
    dfs(x,y+1,sum);
    
    if(vis[x][y]==0) {
            
        //选
        for(int i=0;i<8;i++){
            int tx=x+dx[i],ty=y+dy[i];
            if(tx<1||tx>n||ty<1||ty>m) continue;
            vis[tx][ty]++;
        }

        dfs(x,y+1,sum+g[x][y]);

        for(int i=0;i<8;i++){
            int tx=x+dx[i],ty=y+dy[i];
            if(tx<1||tx>n||ty<1||ty>m) continue;
            vis[tx][ty]--;
        }

这样就能减少递归次数
记得回溯

原来错的细节

  • 我一开始把 ( i , j ) (i,j) (i,j)选上后 就把这一块九个点的vis都记为true 然后在dfs中 如果vis[x][y]==1 就更新ans 然后return 并且写了一个check函数 检查这个点八联通范围内有没有vis为true的点 如果有就跳过这个点

  • 这是完全错误的 因为一个点没选 但是它左边的点选了 这个点也会 v i s = = t r u e vis==true vis==true 那么它右边这个点就会因为它的 v i s = = t r u e vis==true vis==true而被跳过

  • 还有 碰到 v i s [ x ] [ y ] = = 1 vis[x][y]==1 vis[x][y]==1时 不应该直接更新ans并且return 因为vis[x][y]==1只代表这个点不能走 不能直接return 应该再找其他的点dfs 然后再return


http://www.kler.cn/a/584075.html

相关文章:

  • rv1106 PWM控制
  • javaWeb的详细笔记(超详细版本)
  • AI大数据挖掘的威力
  • 【鸿蒙开发】Hi3861学习笔记- GPIO之按键
  • 小白学习:提示工程(什么是prompt)
  • PostgreSQL存储管理体系结构学习笔记2
  • Linux第二次练习
  • hive-进阶版-1
  • 嵌入式开发工程师笔试面试指南-模电基础
  • 查找某个端口是否被占用
  • 【数据结构】4线性表综合实验
  • 前端学习笔记(三)——ant-design vue表单传递数据到父页面
  • 项目组织管理类型-职能式组织和矩阵式组织的区别
  • 单机DeepSeek做PPT,YYDS!
  • 大语言模型的潜力是否被高估
  • C# 发送邮件 报错:此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站。
  • Denoising as Adaptation Noise-Space Domain Adaptation for Image Restoration
  • 【守护蓝色星球】《海洋环境保护法》的重要性与遵守主体
  • Redis三大件 穿透、雪崩、击穿
  • 如何自己做奶茶,从此告别奶茶店