过河卒-动态规划+高精度

http://www.rqnoj.cn/Problem_69.html


题目:过河卒

问题编号:69

题目描述

  如图,A 点有一个过河卒,需要走到目标 B 点。卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。例如上图 C 点上的马可以控制 9 个点(图中的P1,P2 … P8 和 C)。卒不能通过对方马的控制点。 

  棋盘用坐标表示,A 点(0,0)、B 点(n,m)(n,m 为不超过 25 的整数,并由键盘输入),同样马的位置坐标是需要给出的(约定: C<>A,同时C<>B)。现在要求你计算出卒从 A 点能够到达 B 点的路径的条数。

输入格式

键盘输入
   B点的坐标(n,m)以及对方马的坐标(X,Y){不用盘错}

输出格式

 屏幕输出
    一个整数(路径的条数)。

样例输入

6 6 3 2

样例输出

17


一、思路

感觉这是个回溯或者暴力求解的题目,怎么就可以用动态规划,发现还真可以

根据题目和加法原理

f(n,m) = f(n-1,m) + f(n, m-1) 上方和左方的可达路径之和,最优子结构

f(n-1, m) = f(n-2, m) + f(n-1, m-1)

f(n, m-1) = f(n-1, m-1) + f(n, m-2)

重复了f(n-1, m-1) 后面会重复更多,子问题重叠

利用2维数组来描述f[i][j]

怎么来递推求解,初始条件是什么

原本我想从0.0开始向右,向下,发现要考虑的边界条件和马的控制点,太多了


还是从递推式入手容易,初始条件为

f[0][i],第一行,只像右移动

f[i][0],第一列,只像下移动

然后从1,1开始求解,将马足控制点设为-2,此点不走,且,如果上或者左侧点为它,则把它记为0路径相加

if (f[i][j]==-2) continue;

f[i][j] = f[i-1][j]<0 ? 0 : f[i-1][j]

f[i][j] += f[i][j-1]<0 ? 0 : f[i][j-1];


因为题目最大数据为50里取25的排序数,用高精度

所以又加了第三维,来描述位数


二、算法

f[26][26][2],来描述当走到第i,j位置时的路径数f[i][j][2](低位为[0],高位为[1])

结构体 horse[9] 来描述定义,9个马的控制方位,用于初始化为:-2

1、读入b:n,m ;x,y

2、初始化

f[0][i]和f[i][0]和f[0][0]=1

3、动态规划求解,二重循环

第一重:行

第二重:列

       高精度,加法

4、输出结果f[n][m][0-1]

   输出最高位

   补0输出低位


三、错误总结

先是没有用高精度,只有2个得分点

后面用了,是6个得分点

1630 -->1645 输出的结果,比正确的少了十几个路径数

那么高精度和动态规划算法总体应该是没问题的,

最后发现是在初始化行列的时候,将n,m位置写错导致


终于AC

代码如下:

#include <iostream.h>
#include <fstream.h>

struct Point
{
	int x;
	int y;
};

#define BITS 10000000

//输出测试结果
void PrintF(int f[26][26][2], int n, int m)
{
	/*
	cout<<endl;
	for (int i=0; i<=n; i++)
	{
		for (int j=0; j<=m; j++)
		{
			cout<<f[i][j][0]<<"    ";
		}
		cout<<endl;
	}
	cout<<endl;
	*/
}

int main()
{
	int n, m, x, y;
	int f[26][26][2] = {0};
	Point horse[9] = {{0,0}, {-1,-2}, {-1,2}, {1,-2}, {1, 2},
	{2, -1}, {2,1}, {-2, 1}, {-2, -1}};
	int i, j;
   // ifstream inFile("e:\\test.txt");

	//读入B,和horse坐标
	cin>>n>>m>>x>>y;
	//inFile>>n>>m>>x>>y;
	//初始化,马的控制点,设为,-2
	for (i=0; i<9; i++)
	{
		if (x+horse[i].x>=0 && y+horse[i].y>=0)
		{
			f[x+horse[i].x][y+horse[i].y][0] = -2;
		}
	}
	PrintF(f, n, m);
	//初始化第一行
	f[0][0][0] = 1;
    
	for (i=1; i<=m; i++)
	{
		if (f[0][i][0] == -2)
		{
			continue;
		}
		f[0][i][0] = f[0][i-1][0]<0 ? 0 : f[0][i-1][0];
	}
	//初始化第一列
	for (i=1; i<=n; i++)
	{
		if (f[i][0][0] == -2)
		{
			continue;
		}
		f[i][0][0] = f[i-1][0][0]<0 ? 0 : f[i-1][0][0];
	}

    	PrintF(f, n, m);
	//动态规划求解
	//f(i,j)=f(i-1,j)+f(i, j-1);
	for (i=1; i<=n; i++)
	{
		for (j=1; j<=m; j++)
		{
			if (f[i][j][0] == -2)
			{
				continue;
			}
			f[i][j][0] += f[i-1][j][0]<0 ? 0 : f[i-1][j][0];
			f[i][j][1] += f[i-1][j][1];
			f[i][j][0] += f[i][j-1][0]<0 ? 0 : f[i][j-1][0];
			if (f[i][j][0]>BITS)
			{
				f[i][j][1] += f[i][j][0]/BITS;
				f[i][j][0] = f[i][j][0]%BITS;
			}
                        f[i][j][1] += f[i][j-1][1];
		} 
	}

		PrintF(f, n, m);
	//输出结果
	//找到最高位
    i=1;
	while (!f[n][m][i])
	{
		i--;
	}
	//输出最高位
	cout<<f[n][m][i];
	//补位输出低位
	for (j=i-1; j>=0; j--)
	{
	    //判断位数  
        int bit = 10;
		int cnt;
        for (cnt=1; cnt<=7; cnt++)  
        {  
            if (f[n][m][j]/bit==0)  
            {  
                break;  
            }  
            bit *= 10;  
        }  
        //输出0  
        for (int k=1; k<=7-cnt; k++)  
        {  
            cout<<0;  
        }  
        cout<<f[n][m][j];  
	}



	

	//inFile.close();
	return 0;
}


### 使用C++实现过河问题的动态规划解决方案 #### 问题背景 过河问题是经典的动态规划问题之一,涉及在一个二维网格中寻找从起点到终点的有效路径数量。然而,在此问题中存在额外约束条件:某些特定点被标记为不可访问(由“马”的控制区域决定)。这种情况下,我们需要调整标准的动态规划策略来适应新规则。 #### 解决方案概述 为了处理这个问题,我们采用两步走的方式: 1. 初始化阶段:创建一个二维数组 `dp` 来存储每一个格子可能拥有的不同路径数目; 2. 更新过程:遍历整个棋盘并更新每个单元格的状态值基于之前已经计算出来的结果,同时考虑避开那些受控于敌方单位的位置[^3]。 #### 完整代码示例 ```cpp #include <iostream> #include <algorithm> using namespace std; // 协助计算马所能到达点 const int fx[9] = {0, -2, -1, 1, 2, 2, 1, -1, -2}; const int fy[9] = {0, 1, 2, 2, 1, -1, -2, -2, -1}; int ax, ay, mx, my; long long f[30][30]; bool v[30][30]; void initialize() { cin >> ax >> ay >> mx >> my; // 将坐标转换成更易于理解的形式 ax++; ay++; mx++; my++; // 设置原点的路径数为1 f[1][1] = 1; // 标记马能够走到的所有点 v[mx][my] = true; for (int i = 1; i <= 8; ++i) { int newX = mx + fx[i]; int newY = my + fy[i]; if(newX >=1 && newX <=ax && newY>=1&&newY<=ay){ v[newX][newY] = true; } } } void computePaths(){ for(int i=1;i<=ax;i++){ for(int j=1;j<=ay;j++){ // 如果当前点是马能走到的地方,则跳过 if(v[i][j]){ continue; } // 计算当前位置可以从上方或左方过来的情况 if(i-1 >=1 ){ f[i][j]+=f[i-1][j]; } if(j-1 >=1 ){ f[i][j]+=f[i][j-1]; } } } } int main(){ initialize(); computePaths(); // 输出最终答案 cout << f[ax][ay]<<endl; return 0; } ``` 上述代码实现了完整的过河问题求解功能,并且遵循了动态规划的核心思想[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值