A.Mio visits ACGN Exhibition

传送门

题意:该题目的目的是让你求方案数,求方案数的问题其实我们就可以很容易想到动态规划了,因为求方案数一般来说是满足动态规划的两个条件(最优子结构,无后效性)。
那么我们如何考虑状态呢,我们的状态必须要能够表示出所问问题。题目的问题是从左上角走到右下角并且0的数目至少为p,1的数目至少为q的路径数。起点是确定的,那我们只需要考虑终点,那么动态规划数组就增加两个维度表示终点,并且问题又有条件限制,0的数目至少为p,1的数目至少为q,那么我们是否需要考虑再增加两个维度来表示0和1的数目呢?答案是否,因为终点已经确定,那么所走过的点数也就已经确定了,如果我们知道了0的数目,也就可以算出1的数目(总点数减去0的数目),于是只需要再增加一个维度,也就一共是三个维度。
用f[i][j][k]来表示走到位置(i,j)且经过0的个数为k的路径数。

思路:爆搜的复杂度是指数级别的,考虑动态规划。
设f[i][j][k]表示走到位置(i, j)且经过0的个数为k的路径数。
容易得到以下的状态转移方程

  1. 如果(i,j)位置上的数为0, f[i][j][k] = f[i − 1][j][k − 1] + f[i][j − 1][k−1]
  2. 如果(i,j)位置上的数为1, f[i][j][k] = f[i − 1][j][k] + f[i][j − 1][k]

由于0的个数不可能超过步数(n + m -1),所以此时的复杂度是0(nm(n+m-1))的,可以达到要求。
但是所需的空间复杂度不行,考虑用滚动数组,由于求解一个状态时只需要当前行和前一行的状态,我们只需要保存两行的状态就可以解题了。
代码:

#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int N = 510, M = 1010;
int a[N][N];
int f[2][N][M];
int n, m, p, q;
const int mod = 998244353;
int main()
{
    IOS;
    cin>>n>>m>>p>>q;
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1; j <= m; j++)
        {
            cin>>a[i][j];
        }
    }
    int s = 1;//s表示当前行, s^1表示前一行
    //初始化操作
    if(a[1][1] == 1) f[s][1][0] = 1;
    else f[s][1][1] = 1;

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            for(int k = 0; k <= n + m - 1; k++)
            {
                if(i == 1 && j == 1) continue;//排除已经初始化的点
                if(a[i][j] == 1)
                {
                    f[s][j][k] = f[s^1][j][k] + f[s][j-1][k];
                }
                else if(a[i][j] == 0)
                {
                    if(k != 0)
                    {
                        f[s][j][k] = f[s^1][j][k-1] + f[s][j-1][k-1];
                    }
                    else
                    {
                        f[s][j][k] = 0;
                    }
                }
                f[s][j][k] %= mod;
            }
        }
        s ^= 1;
    }
    long long ans = 0;
    for(int k = p; m + n - 1 - k >= q; k++)
    {
        ans += f[s^1][m][k];
        ans %= mod;
    }

    cout<<ans;
}

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值