第19届PTA天梯赛——L2吉利矩阵

本文讲述了如何解决一个与PTA上整数分解相似的问题,利用深度优先搜索和回溯方法,通过遍历矩阵并优化剪枝策略,最终在460毫秒内AC。作者分享了关键思路、细节和代码实现。
摘要由CSDN通过智能技术生成

       

        

        遗憾的是,比赛中没有全部AC。

        现在想出来了,和之前做过的一道题很类似,那道题也是PTA上的题目(整数分解为若干项之和),链接放下面了,大家可以看懂这道题之后去尝试做一下。

PTA | 程序设计类实验辅助教学平台

为什么说类似呢,其实都是深搜+回溯。

我讲一下大概得思路以及注意的细节,然后大家看看代码估计就可以懂啦

当然里面还有一些剪枝,一开始没有优化剪枝会有两个超时(测试点4,5)

思路就是去遍历每一个点,遍历的时候我们给它进行赋值(0~L)即可,然后往下一个点遍历就好了···

第一个细节,题目说的是非负整数(包括0哦)

这里用了一个比较好用的技巧,大家可以记住,以后用起来很方便

样例是3*3的矩阵,我们给他们编号0~8 从左到右 从上倒下

当得到编号i时,我们可以对它进行变换,找到它所在的行列值,具体做法

        x=i/3;y=i%3;大家可以自己算几个,没问题的

当我们遍历到i==M(M是N*N)也就是最后一个的下一个时,我们就赋值完了一种矩阵,这时我们可以去判断是否满足条件,这里用到了hang[],lie[]数组记录各行各列的值,很容易想到,我只要判断他们是否都等与L即可,只要不等于就直接返回,否则最后记得加1记录下来。

这样太慢了,没有剪枝,判断还浪费了O(N)的时间,当然下面的代码就超时了,超时2个测试点

也不能说没有剪枝(有一个小小的剪枝,你们发现没)

    if(hang[x]>L||lie[y]>L)
        return;

#include<iostream>
#include<string.h>
#include<cmath>
using namespace std;
int L,N,M;
int fz[10][10];
int hang[10];
int lie[10];
int sum=0;
void dfs(int i)
{
    if(i==M)
    {
        for(int j=0;j<N;j++)
            if(hang[j]!=L||lie[j]!=L)
                return;
        sum++;
        return;
    }
    int x=i/N,y=i%N;
    if(hang[x]>L||lie[y]>L)
        return;
    for(int j=0;j<=L;j++)
    {
        hang[x]+=j;
        lie[y]+=j;
        dfs(i+1);
        hang[x]-=j;
        lie[y]-=j;
    }
}
int main()
{
    cin>>L>>N;
    M=pow(N,2);
    memset(hang, 0, sizeof(hang));
    memset(lie, 0, sizeof(lie));
    dfs(0);
    cout<<sum;
}

超时就优化一下吧,做做剪枝操作,把判断操作下放到每个行、列的最后一个位置(不理解没关系,其实说的就是这段代码)

        if(x==N-1)
        {
            pl=true;
            j=L-lie[y];
        }
        else if(y==N-1)
        {
            pl=true;
            j=L-hang[x];
        }

大家想一想 当我们到的是的最后一个值时(y==N-1),它的值还有选择吗,是不是就是L-hang[y],使得这一行的值加起来等于L 对吧~     想想 是没问题的

第二个细节出现了,我第一次写,就写反了

错误代码:

        if(x==N-1)
        {
            pl=true;
            j=L-hang[x];
        }
        else if(y==N-1)
        {
            pl=true;
            j=L-lie[y];
        }

有区别嘛~   当x==N-1时指的是最后一行的每一列的最后一个位置 所以我们需要用到lie[y],而不要想当然得认为(x代表行,我看到x就认为是和hang[]有关系的,比如我~ 看了好多遍,(耶? 没问题啊我去,思路剪枝都没问题,怎么答案错误)终于还是让我找到bug了)

具体实现看完整代码吧~没有注释(我知道是坏习惯,但是我太懒了,一般简单的从来不写~,大家还是写注释养成习惯较好哈)

#include<iostream>
#include<string.h>
#include<cmath>
using namespace std;
int L,N,M;
int hang[4],lie[4];
int sum=0;
void dfs(int i)
{
    if(i==M)
    {
        sum++;
        return;
    }
    int x=i/N,y=i%N;
    for(int j=L;j>=0;j--)
    {
        if(hang[x]+j>L||lie[y]+j>L)
            continue;
        bool pl=false;
        if(x==N-1)
        {
            pl=true;
            j=L-lie[y];
        }
        else if(y==N-1)
        {
            pl=true;
            j=L-hang[x];
        }
        hang[x]+=j;
        lie[y]+=j;
        dfs(i+1);
        hang[x]-=j;
        lie[y]-=j;
        if(pl) break;
    }
}
int main()
{
    cin>>L>>N;
    M=pow(N,2);
    memset(hang, 0, sizeof(hang));
    memset(lie, 0, sizeof(lie));
    dfs(0);
    cout<<sum;
    return 0;
}

AC 460ms 搞定La~~~~

大家不懂多多评论区交流,互相请教互相帮助,没准别人一句话就点通你(亲身经历)

 期待和大家共同加油,希望大家不断进步!

  • 41
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值