程序设计天梯赛个人题解 L2-052 吉利矩阵

dfs.
(去年打比赛的时候有点没敢开这个题,今天做起来发现不是很难,当时不知道为什么没思路)

先考虑了一下一维的情况,即需要控制一排数之和的情况,不难发现只需记录当前还差的数量need,即为(N-之前数的总和).且最后一个数直接=need就行.

二维情况稍加延伸. 记录 所需总和 与 每行/列当前和 的差值need.逐行内逐列遍历. 对于每行的最后一列, 直接填充为Hneed(这样既保证了每行的和是N, 又起到了剪枝的作用, 避免对最后一列的数进行从0到Hneed的遍历, 剪掉了递归末端大量的无用分支). 对于最后一行的数, 由于每列的所需和是固定的, 故也可以直接得出.

最后注意一下dfs的写法和细节部分即可

*另注: 检查时使用"Lneed[i]<0"进行筛选, 是因为在最后一列我们直接使用了Hneed, 这样可能导致最后一列的Lneed被减为负值, 故要筛选一下. 这样的情况造成了多余的递归浪费, 可以看到本代码在此部分还有稍微优化和剪枝的空间.

#include <iostream>
#include <algorithm>
using namespace std;
int L;
int N;
int result = 0;
int Hneed[9]{0};
int Lneed[9]{0};
void dfs(int h, int l)
{
    if(h==N-1&&l==0)//倒数第二行最后一个填完后,开始检查
    {
        int sum=0;
        for(int i=0;i<N;i++)
        {
            if(Lneed[i]<0)
                return;
            sum+=Lneed[i];
        }
        if(sum==L)
            result++;
        return;
    }
    if (l == N - 1) // 倒数第一列直接填此行的剩余数
    {
        int i = Hneed[h];
        Hneed[h] -= i;
        Lneed[l] -= i;
        dfs(h + (l + 1) / N, (l + 1) % N);
        Hneed[h] += i;
        Lneed[l] += i;
    }
    else
    {
        int t = min(Hneed[h], Lneed[l]);
        for (int i = 0; i <= t; i++)
        {
            Hneed[h] -= i;
            Lneed[l] -= i;
            dfs(h + (l + 1) / N, (l + 1) % N);
            Hneed[h] += i;
            Lneed[l] += i;
        }
    }
}
int main()
{
    cin>>L;
    cin>>N;
    for(int i=0;i<N;i++)
    {
        Hneed[i]=L;
        Lneed[i]=L;
    }
    dfs(0,0);
    cout<<result;

    return 0;
}

/*赛时代码:
#include <iostream>
#include <algorithm>
using namespace std;
// 一维
//  int get(int nums,int need)//共有nums个数,需要和为need
//  {
//      if(nums<=1)
//          return 1;
//      int res=0;
//      for(int i=0;i<=need;i++)//当前
//      {
//          res+=get(nums-1,need-i);
//      }
//      return res;
//  }
int L;
int N;
int result = 0;
int Hneed[9]{0};
int Lneed[9]{0};

int save[10][10];
void dfs(int h, int l)
{
    //cout<<h<<' '<<l<<endl;
    // if (h >= N - 1) // 倒数第一行不填充就停止
    //     return;
    //if(h==N-2&&l==N-1)//倒数第二行最后一个填完后,开始检查
    if(h==N-1&&l==0)//倒数第二行最后一个填完后,开始检查
    {
        int sum=0;
        for(int i=0;i<N;i++)
        {
            if(Lneed[i]<0)
                return;
            sum+=Lneed[i];
            //save[N-1][i]=Lneed[i];
        }
        //cout<<"checking"<<endl;
        //cout<<"show:"<<endl;
        // for(int i=0;i<N;i++)
        //     for(int j=0;j<N;j++)
        //         cout<<save[i][j]<<" \n"[j==N-1];
        if(sum==L)
        {
            result++;
            //cout<<"pass"<<endl;
        }
        return;
    }
    if (l == N - 1) // 倒数第一列直接填此行的剩余数
    {
        int i = Hneed[h];
        Hneed[h] -= i;
        Lneed[l] -= i;
        //save[h][l]=i;
        dfs(h + (l + 1) / N, (l + 1) % N);
        //save[h][l]=-1;
        Hneed[h] += i;
        Lneed[l] += i;
    }
    else
    {
        int t = min(Hneed[h], Lneed[l]);
        for (int i = 0; i <= t; i++)
        {
            Hneed[h] -= i;
            Lneed[l] -= i;
            //save[h][l]=i;
            dfs(h + (l + 1) / N, (l + 1) % N);
            //save[h][l]=-1;
            Hneed[h] += i;
            Lneed[l] += i;
        }
    }
}
int main()
{
    cin>>L;
    cin>>N;
    for(int i=0;i<N;i++)
    {
        Hneed[i]=L;
        Lneed[i]=L;
    }
    dfs(0,0);
    cout<<result;

    return 0;
}
*/
posted @ 2025-01-27 22:49  艾叶Minerva  阅读(308)  评论(0)    收藏  举报