洛谷p1373:小a和uim之大逃离

题目大意:小a和uim在一个n*m的矩阵内,矩阵的每一个格子有魔液,小a和uim各有一个魔瓶,只能向下或者向右走,逃离的要求是:最后一步由uim走到且小a和uim魔瓶内的魔液等量。魔液量要%(k+1) (题意)。可以从任何一点开始,问有多少种逃离方法。

先从重叠子问题开始思考状态,对于每一个格子i,j,可以是小a走到,也可以是uim走到,他们的魔液分别为a,b。第一步想到用dp[i][j][a][b][k] 来存储这个状态的方法数。定义这个状态的意思是走到 i,j位置,魔液量分别为a,b, 该位置是由小a ( k ==0 ) 走到或uim ( k == 1 ) 走到的方法数。这样定义没问题但是会MLE。
用魔液量的差值代替魔液量,优化一维,dp变成四维,可行。

思考一个问题,差值是有负值的:dp数组本来就是用来存储状态的解,只要对负值做一个映射,使它对应到一个正值,仍然可以不漏任何解而求出正解。

接下来思考转移方程:由于只能向下或者向右走,很容易得到转移方程。
接下来思考边界状态和代码写法:因为起点可以从任意一点开始,且第一步是小a先走,得出初始状态:
dp[i][j][a[i][j]%(k+1)][0]=1;

那么如何递推更新呢?更新时不能遗漏任何一个状态。由于i=1.j=1这个位置,是不能由其他状态走到的,我们可以以这个位置为起始位置,枚举这个位置的所有状态为起始状态,更新其他点的状态。

方便思考就用刷表法写了,刷表法是用已知状态更新未知状态,不需要过多思考边界问题。在有些问题里要对当前状态进行特判judge(),避免用不合法状态来更新合法状态得到错误解。

#include<iostream>
using namespace std;
#include<stdio.h>
#include<string.h>
const int mod=1e9+7;
int n,m,k;
int dp[810][810][17][2];
int a[810][810];
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]);

 	for(int i=1;i<=n;i++)
  		for(int j=1;j<=m;j++)
   			dp[i][j][a[i][j]%(k+1)][0]=1;
   
 	int res=0;
 	for(int i=1;i<=n;i++){
  		for(int j=1;j<=m;j++){
   			for(int t=0;t<=k;t++){
    				for(int g=0;g<2;g++){
     					if(g==0){
      						if(i+1<=n) dp[i+1][j][(t+k+1-a[i+1][j])%(k+1)][1]=(dp[i+1][j][(t+k+1-a[i+1][j])%(k+1)][1]+dp[i][j][t][g])%mod;
      						if(j+1<=m) dp[i][j+1][(t+k+1-a[i][j+1])%(k+1)][1]=(dp[i][j+1][(t+k+1-a[i][j+1])%(k+1)][1]+dp[i][j][t][g])%mod;      
     					}
     					else{
      						if(i+1<=n) dp[i+1][j][(t+a[i+1][j])%(k+1)][0]=(dp[i+1][j][(t+a[i+1][j])%(k+1)][0]+dp[i][j][t][g])%mod;
      						if(j+1<=m) dp[i][j+1][(t+a[i][j+1])%(k+1)][0]=(dp[i][j+1][(t+a[i][j+1])%(k+1)][0]+dp[i][j][t][g])%mod;      
     					}
    				}
   			}
   			res=(res+dp[i][j][0][1])%mod;
  		}
 	}
 	printf("%d\n",res);
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值