舞师表演!

问题描述

春节期间,舞狮表演是必不可少的节目。今年,小蓝所在的村子也组织了一场盛大的舞狮表演。

村里的广场被划分成了一个 n×nn×n 大小的网格。每个格子上都放着一个红包,里面装着不同金额的钱。

为了让表演更加精彩,村长决定设计一条特别的舞狮路线。舞狮队伍需要从左上角的格子出发,一路向下或向右移动,最终到达右下角的格子。

然而,“狮子们”很挑剔,它们只会在装着奇数金额钱的格子上表演。因此,如果格子上装着偶数金额的钱,小蓝就需要在舞狮队伍开始移动前,偷偷地往里面塞钱。但为了不引起围观群众的注意,他每次塞钱,必须给一整行的格子里的红包都塞钱(每个红包塞一块钱)。

现在,小蓝想知道,他最少需要塞多少钱,才能让狮子们顺利地完成表演?如果无论如何也无法让狮子们完成表演,则输出 NO!

输入格式

第一行包含一个整数 tt (1≤t≤102)(1≤t≤102),表示测试用例的数量。

每个测试用例的第一行包含一个正整数 nn(1≤n≤1031≤n≤103),表示广场网格的大小。

接下来的 nn 行,每行包含 nn 个整数 ai,jai,j​(1≤ai,j≤1051≤ai,j​≤105),表示对应格子的红包金额。

数据保证输入的所有的 n2n2 的总和不超过 106106。

输出格式

对于每个测试用例,输出一行。如果可以完成表演,输出一个整数,表示小蓝最少需要塞多少钱;否则输出 NO!

    我们从右下角开始,它可能是由上面的格子下来,也可能由左边的各自过来,这两个分解的子问题都和原问题类似。然后我们再分解,就会得到一系列相似,规模更小的子问题。

状态定义:我们用dp[i][j]代表到达第i行第i列格子需要的最小花费。dp[i][j]是由dp[i-1][j]和dp[i][j-1]两种状态得到的。

如果是从上方下来的:

    如果nums[i][j]为奇数,则dp[i][j]=min(dp[i][j],dp[i-1][j])

    否则dp[i][j]=min(dp[i][j],dp[i-1][j]+n)

如果是从左边过来:

    我们需要考虑左边的格子的奇偶性,例如左边的格子为偶数,而当前格子为奇数,则无法到达当前格子。当前格子与左边的格子需保持奇偶一致性。

   dp[i][j]=min(dp[i][j],dp[i][j-1])

这样我们就得到了完整的状态转移方程。

#include<iostream>

#include<cstring>

#include<vector>

#define INT_MAX 1e6

using namespace std;

int m,n;

void solve(vector<vector<int>> nums,vector<vector<int>> dp)

{

      dp[0][0]=nums[0][0]%2==0? n: 0;

      for(int i=0;i<n;i++)

      {

        for(int j=0;j<n;j++)

        {

          bool flag=nums[i][j]%2==0;

          //从上方过来

          if(i>0&&dp[i-1][j]!=INT_MAX)

          {

             if(flag) dp[i][j]=min(dp[i][j],dp[i-1][j]+n);

             else dp[i][j]=min(dp[i][j],dp[i-1][j]);

          }

         //从左边过来

         if(j>0&&(nums[i][j]%2==nums[i][j-1]%2)&&dp[i][j-1]!=INT_MAX)

         {

           dp[i][j]=min(dp[i][j],dp[i][j-1]);

         }

        }

      }

      if(dp[n-1][n-1]==INT_MAX)

      cout<<"NO!"<<endl;

      else

      cout<<dp[n-1][n-1]<<endl;

}

int main()

{

    cin>>m;

    while(m--)

    {

      cin>>n;

      vector<vector<int>> arr(n,vector<int> (n));

      vector<vector<int>> dp(n,vector<int> (n,INT_MAX));

      for(int i=0;i<n;i++)

      {

        for(int j=0;j<n;j++)

        {

          cin>>arr[i][j];

        }

      }

      solve(arr,dp);

    }

    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值