洛谷P1373 小a和uim之大逃离

在一场生死考验中,小a和uim需要通过策略性的选择路径和收集魔液来确保双方存活。本文介绍了一道关于动态规划的问题,通过巧妙的设计达到平衡双方魔液收集量的目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目背景

  小a和uim来到雨林中探险。突然一阵北风吹来,一片乌云从北部天边急涌过来,还伴着一道道闪电,一阵阵雷声。刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个披头散发、青面獠牙的怪物,低沉着声音说:“呵呵,既然你们来到这,只能活下来一个!”。小a和他的小伙伴都惊呆了!

题目描述

  瞬间,地面上出现了一个n*m的巨幅矩阵,矩阵的每个格子上有一坨0~k不等量的魔液。怪物各给了小a和uim一个魔瓶,说道,你们可以从矩阵的任一个格子开始,每次向右或向下走一步,从任一个格子结束。开始时小a用魔瓶吸收地面上的魔液,下一步由uim吸收,如此交替下去,并且要求最后一步必须由uim吸收。魔瓶只有k的容量,也就是说,如果装了k+1那么魔瓶会被清空成零,如果装了k+2就只剩下1,依次类推。怪物还说道,最后谁的魔瓶装的魔液多,谁就能活下来。小a和uim感情深厚,情同手足,怎能忍心让小伙伴离自己而去呢?沉默片刻,小a灵机一动,如果他俩的魔瓶中魔液一样多,不就都能活下来了吗?小a和他的小伙伴都笑呆了!
  现在他想知道他们都能活下来有多少种方法。
输入输出格式
输入格式:
第一行,三个空格隔开的整数n,m,k
接下来n行,m列,表示矩阵每一个的魔液量。同一行的数字用空格隔开。
输出格式:
一个整数,表示方法数。由于可能很大,输出对1 000 000 007取余后的结果。
输入输出样例
输入样例#1:
2 2 3
1 1
1 1
输出样例#1:
4
说明

【数据范围】

对于20%的数据,n,m<=10,k<=2

对于50%的数据,n,m<=100,k<=5

对于100%的数据,n,m<=800,1<=k<=15

解题报告:

  很水的DP啊!一眼秒。F[i][j][k]表示DP到(i,j)时,小明和小伙伴的差值为k的方案数,随便递推递推就是了。
  不过恶心的地方在这题卡内存。你必须知道一个性质:就是差值kk是等价的。因为你随时可以把两个人混着看。有这个性质在才能少开一半数组。否则就必须做两遍。做两遍的话就会有2个点T ……晕。
代码:

90分代码。T了两个点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Mod 1000000007
using namespace std;
int dp[801][801][37],n,m,K,value[801][801],ans;
int main(){
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&value[i][j]);
    //起点为偶数 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if((i+j)%2==0)
                dp[i][j][value[i][j]+16]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            for(int k=-K;k<=K;k++){
                if((i+j)%2==0)//下一步是小伙伴 
                {   
                    if(j<m) dp[i][j+1][(k-value[i][j+1])%(K+1)+16]=(dp[i][j+1][(k-value[i][j+1])%(K+1)+16]+dp[i][j][k+16])%Mod;
                    if(i<n) dp[i+1][j][(k-value[i+1][j])%(K+1)+16]=(dp[i+1][j][(k-value[i+1][j])%(K+1)+16]+dp[i][j][k+16])%Mod;
                }
                else  //下一步是小明 
                {
                    if(j<m) dp[i][j+1][(k+value[i][j+1])%(K+1)+16]=(dp[i][j+1][(k+value[i][j+1])%(K+1)+16]+dp[i][j][k+16])%Mod;
                    if(i<n) dp[i+1][j][(k+value[i+1][j])%(K+1)+16]=(dp[i+1][j][(k+value[i+1][j])%(K+1)+16]+dp[i][j][k+16])%Mod;
                }

            }
            if((i+j)%2)ans=(ans+dp[i][j][16])%Mod;
    }
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if((i+j)%2)
                dp[i][j][value[i][j]+16]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            for(int k=-K;k<=K;k++){
                if((i+j)%2)//下一步是小伙伴 
                {   
                    if(j<m) dp[i][j+1][(k-value[i][j+1])%(K+1)+16]=(dp[i][j+1][(k-value[i][j+1])%(K+1)+16]+dp[i][j][k+16])%Mod;
                    if(i<n) dp[i+1][j][(k-value[i+1][j])%(K+1)+16]=(dp[i+1][j][(k-value[i+1][j])%(K+1)+16]+dp[i][j][k+16])%Mod;
                }
                else  //下一步是小明 
                {
                    if(j<m) dp[i][j+1][(k+value[i][j+1])%(K+1)+16]=(dp[i][j+1][(k+value[i][j+1])%(K+1)+16]+dp[i][j][k+16])%Mod;
                    if(i<n) dp[i+1][j][(k+value[i+1][j])%(K+1)+16]=(dp[i+1][j][(k+value[i+1][j])%(K+1)+16]+dp[i][j][k+16])%Mod;
                }
            }
            if((i+j)%2==0)ans=(ans+dp[i][j][16])%Mod;
        }
    printf("%d\n",ans);            
}

100分代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

const int max_n = 801;
const int max_k = 16;
const int inf = 1e9+7;

int f[max_n][max_n][max_k][2];
int a[max_n][max_n];
int n,m,k,ans;

inline void dp1()
{
    for(int i=1; i<=n; ++i)
      for(int j=1; j<=m; ++j)
        for(int g=0; g<=k-1; ++g)
        {
            f[i][j][g][0]=(f[i][j][g][0]+f[i-1][j][(g-a[i][j]+k)%k][1])%inf;
            f[i][j][g][0]=(f[i][j][g][0]+f[i][j-1][(g-a[i][j]+k)%k][1])%inf;
            f[i][j][g][1]=(f[i][j][g][1]+f[i-1][j][(g+a[i][j])%k][0])%inf;
            f[i][j][g][1]=(f[i][j][g][1]+f[i][j-1][(g+a[i][j])%k][0])%inf;
        }
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<=n; ++i)
      for(int j=1; j<=m; ++j)
      {
          scanf("%d",&a[i][j]);
          f[i][j][a[i][j]][0]=1;
      }
    k+=1;
    dp1();
    for(int i=1; i<=n; ++i)
      for(int j=1; j<=m; ++j)
        ans=(ans+f[i][j][0][1])%inf;
    printf("%d\n",ans);
    return 0;
}
资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 华为移动服务(Huawei Mobile Services,简称 HMS)是一个全面开放的移动服务生态系统,为企业开发者提供了丰富的工具 API,助力他们构建、运营推广应用。其中,HMS Scankit 是华为推出的一款扫描服务 SDK,支持快速集成到安卓应用中,能够提供高效且稳定的二维码条形码扫描功能,适用于商品扫码、支付验证、信息获取等多种场景。 集成 HMS Scankit SDK 主要包括以下步骤:首先,在项目的 build.gradle 文件中添加 HMS Core 库 Scankit 依赖;其次,在 AndroidManifest.xml 文件中添加相机访问互联网访问权限;然后,在应用程序的 onCreate 方法中调用 HmsClient 进行初始化;接着,可以选择自定义扫描界面或使用 Scankit 提供的默认扫描界面;最后,实现 ScanCallback 接口以处理扫描成功失败的回调。 HMS Scankit 内部集成了开源的 Zxing(Zebra Crossing)库,这是一个功能强的条码二维码处理库,提供了解码、生成、解析等多种功能,既可以单独使用,也可以与其他扫描框架结合使用。在 HMS Scankit 中,Zxing 经过优化,以更好地适应华为设备,从而提升扫描性能。 通常,ScanKitDemoGuide 包含了集成 HMS Scankit 的示例代码,涵盖扫描界面的布局、扫描操作的启动停止以及扫描结果的处理等内容。开发者可以参考这些代码,快速掌握在自己的应用中实现扫码功能的方法。例如,启动扫描的方法如下: 处理扫描结果的回调如下: HMS Scankit 支持所有安卓手机,但在华为设备上能够提供最佳性能体验,因为它针对华为硬件进行了
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值