洛谷:P1002 过河卒 c++题解 动态规划

题目详情:

1.深度优先搜索。

我看到题目第一反应就是用深度优先搜索,于是写了如下代码:

#include<iostream>
using namespace std;
int n, m, a, b;
int map[21][21]={0};
int movx[8] = { 2,1,-1,-2,-2,-1,1,2 };
int movy[8] = { 1,2,2,1,-1,-2,-2,-1 };

int ans;
void dfs(int x, int y) {
	if (x == n && y == m) {
		ans += 1;
		return;
	}

	if (x > n || y > m) {
		return;
	}
	int tempx, tempy;
	tempx = x + 1;
	tempy = y + 1;

	if (tempx<=n&&!map[tempx][y]) {
		dfs(tempx, y);
	}
	if (tempy <= m && !map[x][tempy]) {
		dfs(x, tempy);
	}

	return;

}
int main() {
	cin >> n >> m >> a >> b;

	map[a][b] = 1;

	int tempx, tempy;
	for (int i = 0; i < 8; i++) {
		tempx = a + movx[i];
		tempy = b + movy[i];

		if (tempx >= 1 && tempx <= n && tempy >= 1 && tempy <= m) {
			map[tempx][tempy] = 1;
		}
	}
	ans = 0;

	dfs(0, 0);
	
	cout << ans << endl;


	return 0;
}

不出我所料,TLE了....

感觉dfs没办法再优化了,做不下去,就可以试试动态规划。

2.动态规划

1).采取一个地图map[n][m]来存储当前图的状态,如果不处于‘马的控制点’,则让map[i][j]=0;

,否则map[i][j]=1;

2)分析:

        由于A 只能往 下/右 两个方向走,则 到达B 只能从 B的上面 或者左边。

其实可以观察得出,到B(B的坐标为n,m)的通路 就等于 从上面到B的通路总数 + 从 左边到到B的通路总数。

而其他节点也是一样,先设定:

        arr[i][j][0] :从位置(i,j)上方到达位置(i,j)的通路总数

         arr[i][j][1] :从位置(i,j)左边到达位置(i,j)的通路总数

则由(0,0)到达(i.j)的所有通路数为:arr[i][j][0] + arr[i][j][1]

可以得到一个递推公式:

arr[i][j][0] = arr[i-1][j][0]+arr[i-1][j][1];

arr[i][j][1] = arr[i][j-1][0]+arr[i][j-1][1];

则答案应该为 arr[n][m][0]+arr[n][m][1]

3).边界处理:

  1.  arr[0][0][1]=arr[0][0][0] = 0;
  2. arr[i][0][0]=1,arr[i][0][1]=0; (0<i<=n)
  3. arr[0][j][0]=0;arr[0][j][1]=1; (0<j<=m)
  4. 代码如下:

arr[0][0][0] = arr[0][0][1] = 0;
for (int i = 1; i <= n; i++) {		
	arr[i][0][0] = 1;
	arr[i][0][1] = 0;
}

for (int i = 1; i <= m; i++) {

	arr[0][i][0] = 0;
	arr[0][i][1] = 1;
}

4).一些细节:

 (1).开 long long 

 (2).在边界处理的时候,有一些细节要处理,我就被坑了好久..

        如果‘马的控制点’是在0这个边界上面的话,那么代码应该改为:

arr[0][0][0] = arr[0][0][1] = 0;
	for (int i = 1; i <= n; i++) {
		if (map[i][0]) {
			for (int j = i; j <= n; j++) {
				arr[j][0][0] = arr[j][0][1] = 0;
			}
			break;
		}
		arr[i][0][0] = 1;
		arr[i][0][1] = 0;
	}

	for (int i = 1; i <= m; i++) {
		if (map[0][i]) {
			for (int j = i; j <= m; j++) {
				arr[0][j][0] = arr[0][j][1] = 0;
			}
			break;
		}
		arr[0][i][0] = 0;
		arr[0][i][1] = 1;
	}
	

因为如果边界上有一点为马的控制点,则剩余的节点的路都被截断了.

5).最终代码如下:

#include<iostream>
using namespace std;
int n, m, a, b;
int map[21][21]={0};
int movx[8] = { 2,1,-1,-2,-2,-1,1,2 };
int movy[8] = { 1,2,2,1,-1,-2,-2,-1 };
long long arr[21][21][2];
//arr[i][j][0]:从i,j上面 到达 i,j 的通路总数
//arr[i][j][1]:从i,j左边 到达 i,j 的通路总数


int main() {
	cin >> n >> m >> a >> b;

	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++)
			map[i][j] = 0;
	}

	map[a][b] = 1;

	int tempx, tempy;
    //标记马的控制点
	for (int i = 0; i < 8; i++) {
		tempx = a + movx[i];
		tempy = b + movy[i];

		if (tempx >= 0 && tempx <= n && tempy >= 0 && tempy <= m) {
			map[tempx][tempy] = 1;
		}
	}

	//初始化边界
	arr[0][0][0] = arr[0][0][1] = 0;
	for (int i = 1; i <= n; i++) {
		if (map[i][0]) {
			for (int j = i; j <= n; j++) {
				arr[j][0][0] = arr[j][0][1] = 0;
			}
			break;
		}
		arr[i][0][0] = 1;
		arr[i][0][1] = 0;
	}

	for (int i = 1; i <= m; i++) {
		if (map[0][i]) {
			for (int j = i; j <= m; j++) {
				arr[0][j][0] = arr[0][j][1] = 0;
			}
			break;
		}
		arr[0][i][0] = 0;
		arr[0][i][1] = 1;
	}
	
    //递推方程
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			
			if (!map[i - 1][j]) {
                //up,上面是可到达点
				arr[i][j][0] = arr[i - 1][j][0] + arr[i - 1][j][1];
			}
			else {
				arr[i][j][0] = 0;
			}
			
			
			
			if (!map[i][j - 1]) {
				//left左边是可到达点
				arr[i][j][1] = arr[i][j - 1][0] + arr[i][j - 1][1];
			}
			else {
				arr[i][j][1] = 0;
			}
			
			
		}
	}
	cout << arr[n][m][0] + arr[n][m][1] << endl;


	return 0;
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值