【1错笔记】过河卒——动态规划、BOOL记录数组

过河卒

原题链接:传送

题目描述

棋盘上 AA 点有一个过河卒,需要走到目标 BB 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 CC 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,AA 点 (0, 0)(0,0)、BB 点 (n, m)(n,m),同样马的位置坐标是需要给出的

现在要求你计算出卒从 AA 点能够到达 BB 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 B 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1

6 6 3 3

输出 #1

6

说明/提示

对于 %100 的数据,1≤n,m≤20,0≤ 马的坐标 ≤20。


问题分析:

第一个错误是错在题意的理解上面,虽然这道题的背景是中国象棋,但是在看到卒过河后只能向下和向右时就有点怀疑,到底是为了防止陷入左右左右左的死循环,还是为了提醒我们这不是中国象棋的规则呢。

结果就是我想多了,对后面马的行走方式“跳跃一步”我以为就是上下左右的一步而已,而不是日字走法,其实还忽略了图的重要性,图中已经标明了马的控制点位置。

第二个困难就是这是我第一次用C++写题,还有一些语法的问题。

这是我第一次写的代码

#include <iostream>
#include <vector>
using namespace std;

int main(){
	

	int x1,x2,y1,y2;
	int i,j;
	
	cin >> x1;
	cin >> y1;
	cin >> x2;
	cin >> y2;
	
	vector< vector<long long int> > a(x1+1);
	for ( i =0; i<(x1+1); i++ )
	{
		a[i].resize(y1+1);
	}
	
	for ( i=0; i<(x1+1); i++ )
	{
		a[i][0] = 1;
	}
	for ( j=0; j<(y1+1); j++ )
	{
		a[0][j] = 1;
	}
	
	for ( i=1; i<(x1+1); i++ )
		for ( j=1; j<(y1+1); j++ )
		{
			a[i][j] = a[i-1][j] + a[i][j-1];
		for ( i=x2-1; i<=(x2+1); i++ )
			for ( j=y2-1; j<=(y2+1); j++ )
				a[i][j] = 0;
		}
	
	cout << a[x1][y1];
	return 0;

} 

其实这个很容易就可以得出状态转移方程,即

a[i][j] = a[i-1][j] + a[i][j-1];

然后因为之前有数组越界的经验,所以就将第一行和第一列都初始化为1,从1,1开始算起(其实这里就错了,因为可能第一行和第一列中就有马的控制点,又因为卒不能上走,故若在第一行控制点的右边全为0),马的行走方式也误认为是上下左右一格,每写完一个的数值后动态刷新马控制点为0.

虽然整个思路也好,代码也好都是错的,但运行不了!!哪怕是简单的数据也严重超时,看了好几遍也不知道到底问题出现在哪里,估计是vector没用好,但水平有限真的检查不出来(哭)。

然后就还是换用普通的数组了(以最大数值情况创建数组),也改正了马的行走方式,依旧采用每次写入都动态刷新控制点的做法,代码如下:

#include <iostream>
#include <cstdio>
using namespace std;

int fx[]={0,-2,-2,-1,-1,1,1,2,2};
int fy[]={0,1,-1,2,-2,2,-2,1,-1};
long long int a[21][21];
int i,j;

void reset(int x2, int y2);

int main(){
	int x1,x2,y1,y2;
	
	cin>>x1>>y1>>x2>>y2;
	
	for ( i=0; i<(x1+1); i++ )
		a[i][0]=1;
	for ( i=0; i<(y1+1); i++ )
		a[0][i]=1;
		
	for ( i=1; i<(x1+1); i++ )
		for ( j=1; j<(y1+1); j++ )
		{
			a[i][j] = a[i-1][j] + a[i][j-1];
			reset(x2,y2);
		}
	
	cout<<a[x1][y1]<<endl;
	
	return 0;	
} 

void reset(int x2, int y2){
	for ( i=0; i<9; i++ )
		{
			a[x2+fx[i]][y2+fx[i]] = 0;
		}
}

虽然这次解决了马行走方式的问题,但还是没有解决一个问题,就是因为可能第一行和第一列中就有马的控制点,又因为卒不能上走,故若在第一行控制点的右边全为0。

那么现在我的问题就是如何找到新的算法解决第0行和列越界的情况,最终还是求助于洛谷的题解,看了别人的解法后都然大悟!!

既然我要解决越界问题,其实直接判断这到底是不是第0行或列,然后揪出来特殊处理不就好了。题解的方法十分巧妙,将状态转移方程直接拆成两个部分,分别对应行和列。

题解中还解决了一个我很担心的问题,缩短了时间复杂度,就是每次的reset,在我上面的解法中,我每次通过状态转移方程写入数据时都要把马的9个控制点重新归零,要1个循环,九个步骤,特别担心超时。但题解中直接利用bool数组记录,只用循环一次即可,同时为了防止越界,也限制了条件在前。


AC代码:

#include <iostream>
#include <cstdio>
using namespace std;

//马的9个控制点 
int fx[]={0,-2,-2,-1,-1,1,1,2,2};
int fy[]={0,1,-1,2,-2,2,-2,1,-1};
//只有BP[0][0]被赋值为1,其余均为0,这是全局变量的特性 
long long int BP[21][21]={1};
//同样利用的是全局变量的特性
//但因为这个其实和最后的运算相反,需要再用到!
//当然,也可以这里做处理,for循环赋初始值为1 
bool MARK[21][21]; 

int main(){
	int x1,x2,y1,y2;
	int i,j;
	

	cin>>x1>>y1>>x2>>y2;
	
	//通过MARK数组记录马的9个控制点 
	for ( i=0; i<9; i++ )
	{
		//if判断是为了防止数组发生越界,只能在表盘范围内的点,越界就不管啦 
		if ( x2+fx[i]>=0 && x2+fx[i]<=x1 && y2+fy[i]>=0 && y2+fy[i]<=y1 )
			MARK[x2+fx[i]][y2+fy[i]] = 1;
	}
	
	for ( i=0; i<=x1; i++ )
		for ( j=0; j<=y1; j++ )
		{
			if(i)
				BP[i][j] += BP[i-1][j];
			if(j)
				BP[i][j] += BP[i][j-1];
			//特别重要的一个地方!
			//当该点被刚刚标记为控制点后,就要归零,这里用的是乘法 
			BP[i][j] *= !MARK[i][j];
		}
			
	cout<<BP[x1][y1];
	return 0;		

} 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值