POJ 1915 Knight Moves

Knight Moves
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 18211 Accepted: 8335

Description

Background
Mr Somurolov, fabulous chess-gamer indeed, asserts that no one else but him can move knights from one position to another so fast. Can you beat him?
The Problem
Your task is to write a program to calculate the minimum number of moves needed for a knight to reach one point from another, so that you have the chance to be faster than Somurolov.
For people not familiar with chess, the possible knight moves are shown in Figure 1.

Input

The input begins with the number n of scenarios on a single line by itself.
Next follow n scenarios. Each scenario consists of three lines containing integer numbers. The first line specifies the length l of a side of the chess board (4 <= l <= 300). The entire board has size l * l. The second and third line contain pair of integers {0, ..., l-1}*{0, ..., l-1} specifying the starting and ending position of the knight on the board. The integers are separated by a single blank. You can assume that the positions are valid positions on the chess board of that scenario.

Output

For each scenario of the input you have to calculate the minimal amount of knight moves which are necessary to move from the starting point to the ending point. If starting point and ending point are equal,distance is zero. The distance must be written on a single line.

Sample Input

3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1

Sample Output

5
28
0
 
这个也是广搜,特点是:这个题的棋盘大小需要输入。另外,因为棋盘比较大,所以会有重复部分,这个要在注意。应该删除那些重复部分,这样效率就会提高了!
AC代码:
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
struct init
{
    int x,y,step;
}poi1,poi2;
int m,n,i,k,j,t;
int dir[8][2]={{-2,1},{-2,-1},{-1,2},{-1,-2},{1,2},{1,-2},{2,1},{2,-1}};
int visited[400][400],flag;
int bound(int x,int y)
{
	if(x>=0&&x<n&&y>=0&&y<n)return 1;
	else return 0;
}
void bfs()
{
	flag=0;
	memset(visited,0,sizeof(visited));
	visited[poi1.x][poi1.y]=1;
	poi1.step=0;
	poi2.step=n*n;
	queue<init>q;
	q.push(poi1);
    init s1,s2;
	while(!q.empty())
	{
		s1=q.front();
		q.pop();
		for(i=0;i<8;i++)
		{
			s2.x=s1.x+dir[i][0];
			s2.y=s1.y+dir[i][1];
			s2.step=s1.step+1;
			if(bound(s2.x,s2.y)==1&&visited[s2.x][s2.y]==0)
			{
				visited[s2.x][s2.y]=1;
				q.push(s2);
				if(s2.x==poi2.x&&s2.y==poi2.y)
					if(s2.step<poi2.step)
						poi2.step=s2.step;
			}
		}
	}
}
int main()
{
	scanf("%d",&t);
	for(k=0;k<t;k++)
	{
		scanf("%d",&n);
		scanf("%d %d",&poi1.x,&poi1.y);
		scanf("%d %d",&poi2.x,&poi2.y);
		if(poi1.x==poi2.x&&poi1.y==poi2.y)
			printf("0\n");
		else
		{
			bfs();
			printf("%d\n",poi2.step);
		}
	}
	return 0;
}

### POJ1915 解题思路 POJ1915 是关于图论中的强连通分量问题,主要涉及 Tarjan 算法的应用以及如何通过缩点使有向无环图(DAG)变为强连通图。以下是该问题的核心算法思路: #### 问题描述 题目要求计算在一个有向图中,最少需要添加多少条边才能使得整个图成为强连通图。 --- #### 核心算法分析 1. **Tarjan 缩点** 使用 Tarjan 算法找到所有的强连通分量并将其缩成单个节点,从而得到一个新的有向无环图(DAG)。此过程可以有效简化原始图结构[^2]。 2. **统计入度和出度为零的节点数** 对于新的 DAG 图,分别统计入度为零的节点数量 \(in\_zero\) 和出度为零的节点数量 \(out\_zero\)。这两个值决定了需要增加的边的数量。 3. **计算所需新增边数** 新增边的数量取决于以下两种情况的最大值: \[ answer = \max(in\_zero, out\_zero) \] 这是因为每一条新加入的边都可以消除一个入度或出度为零的情况。 4. **特殊情况处理** 如果初始图已经是强连通图,则无需任何操作,即答案为 0。 --- #### 实现细节 下面提供了一个基于 C++ 的实现框架,展示了如何利用 Tarjan 算法解决这个问题: ```cpp #include <iostream> #include <vector> #include <stack> using namespace std; const int MAXN = 1e4 + 5; int n, m, idx, scc_cnt; int dfn[MAXN], low[MAXN]; bool in_stack[MAXN]; vector<int> adj[MAXN]; stack<int> stk; vector<vector<int>> SCC; // 存储每个强连通分量 void tarjan(int u) { dfn[u] = low[u] = ++idx; stk.push(u); in_stack[u] = true; for (auto &v : adj[u]) { if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in_stack[v]) { low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { vector<int> component; while (true) { int t = stk.top(); stk.pop(); in_stack[t] = false; component.push_back(t); if (t == u) break; } SCC.push_back(component); scc_cnt++; } } // 主函数逻辑 int main() { cin >> n >> m; for (int i = 0; i < m; ++i) { int a, b; cin >> a >> b; adj[a].push_back(b); } // 初始化 Tarjan 参数 memset(dfn, 0, sizeof(dfn)); memset(in_stack, false, sizeof(in_stack)); idx = scc_cnt = 0; // 执行 Tarjan 算法 for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } // 构建 DAG 并统计出入度 int indegree_zero = 0, outdegree_zero = 0; vector<int> dag_in(scc_cnt + 1, 0), dag_out(scc_cnt + 1, 0); for (int u = 1; u <= n; ++u) { for (auto &v : adj[u]) { if (SCC[dfn[u]].back() != SCC[dfn[v]].back()) { // 不同强连通分量之间才有边 dag_in[SCC[dfn[v]].back()]++; dag_out[SCC[dfn[u]].back()]++; } } } for (int i = 1; i <= scc_cnt; ++i) { if (dag_in[i] == 0) indegree_zero++; if (dag_out[i] == 0) outdegree_zero++; } cout << max(indegree_zero, outdegree_zero); // 输出结果 } ``` --- #### 关键点解释 - **Tarjan 算法的作用** Tarjan 算法能够高效地找出所有强连通分量,并将它们压缩成单一节点,便于后续的操作[^2]。 - **为什么取最大值?** 添加一条边最多能减少一个入度为零或者出度为零的节点。因此,最终的结果应为两者之间的较大者。 - **时间复杂度** 整体的时间复杂度为 \(O(n+m)\),其中 \(n\) 表示节点数,\(m\) 表示边数。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值