按位异或等于 K 的给定矩阵的可能路径计数
给定一个N*M (N + M ≤ 40)矩阵mat[][] ,每个单元格都有一些从0 到 10 18的值和一个整数K 。查找所有可能路径的计数,使得路径中元素的按位 XOR 等于K。仅允许向右和向下移动。
例子:
Input: N = 3, M = 4, K= 2
mat[][] = { {1, 3, 3, 3},
{0, 3, 3, 2},
{3, 0, 1, 1} }
Output: 5
Explanation: All possible paths are:
(1,1)→(2,1)→(2,2)→(3,2)→(3,3)→(3,4) = 1^0 ^3^0^1^1 = 2
(1,1)→(2,1)→(3,1)→(3,2)→(3,3)→(3,4) = 1^0^3^0^1^1 = 2
(1,1)→(2,1)→(2,2)→(2,3)→(2,4)→(3,4) = 1^0^3^3^2^1 =2
(1,1)→(1,2)→(1,3)→(2,3)→(3,3)→(3,4) = 1^3^3^3^1^1 =2
(1,1)→(1,2)→(2,2)→(2,3)→(3,3)→(3,4) = 1^3^3^3^1^1 =2
Input: N = 3, M = 3, K =11
mat[][] = { {2, 1, 5},
{7, 10, 0},
{12, 6, 4} }
Output: 3
Explanation:All possible paths are:
(1,1)→(2,1)→(3,1)→(3,2)→(3,3) = 2 ^ 7 ^12 ^ 6 ^4 =11
(1,1)→(2,1)→(2,2)→(2,3)→(3,3) = 2 ^ 7 ^ 10 ^ 0 ^4 =11
(1,1)→(1,2)→(2,2)→(3,2)→(3,3) = 2^1^10^ 6 ^ 4 = 11
朴素方法:简单的方法是遍历所有可能的路径并检查该路径的 XOR 是否等于K。在这种情况下,使用回溯,因为在任何给定的单元格中都会有两种选择,要么向右或向下,这将成为回溯解决方案中的两个递归语句。
以下是上述方法的实现:
C++
// C++ code to implement the above approach
#include
using namespace std;
#define ll long long int
ll n, m, k;
vector> mat;
// Backtracking to find the path count
void backtracking(ll i, ll j, ll zor, ll &ans)
{
// If the bottom-right cell is reached
if (i == n - 1 && j == m - 1) {
// If XOR value is k
if (zor == k)
ans++;
return;
}
// Move rightwards
if (j + 1 < m)
backtracking(i, j + 1,
zor ^ mat[i][j + 1],
ans);
// Move downwards
if (i + 1 < n)
backtracking(i + 1, j,
zor ^ mat[i + 1][j],
ans);
}
// Function to calculate all possible paths
int countPaths(int N, int M, int K)
{
ll ans = 0;
n = N; m = M; k = K;
if (N == 1 && M == 1
&& mat[0][0] == K) {
return 1;
}
// Calling the backtracking function
backtracking(0, 0, mat[0][0], ans);
return ans;
}
// Driver Code
int main()
{
int N = 3, M = 3, K = 11;
mat = { {2, 1, 5}, {7, 10, 0},
{12, 6, 4} };
// Function call
int ans = countPaths(N, M, K);
cout << ans << "\n";
return 0;
}
Java
// Java code to implement the above approach
import java.util.*;
class GFG{
static int n, m, k , ans;
static int [][]mat;
// Backtracking to find the path count
static void backtracking(int i, int j, int zor)
{
// If the bottom-right ceint is reached
if (i == n - 1 && j == m - 1) {
// If XOR value is k
if (zor == k)
ans++;
return;
}
// Move rightwards
if (j + 1 < m)
backtracking(i, j + 1,
zor ^ mat[i][j + 1]);
// Move downwards
if (i + 1 < n)
backtracking(i + 1, j,
zor ^ mat[i + 1][j]);
}
// Function to calculate all possible paths
static int countPaths(int N, int M, int K)
{
ans = 0;
n = N; m = M; k = K;
if (N == 1 && M == 1
&& mat[0][0] == K) {
return 1;
}
// Calling the backtracking function
backtracking(0, 0, mat[0][0]);
return ans;
}
// Driver Code
public static void main(String[] args)
{
int N = 3, M = 3, K = 11;
mat = new int[][]{ {2, 1, 5}, {7, 10, 0},
{12, 6, 4} };
// Function call
int ans = countPaths(N, M, K);
System.out.print(ans+ "\n");
}
}
// This code is contributed by 29AjayKumar
C#
// C# code to implement the above approach
using System;
class GFG{
static int n, m, k, ans;
// Backtracking to find the path count
static void backtracking(int i, int j, int zor, int [,]mat)
{
// If the bottom-right ceint is reached
if (i == n - 1 && j == m - 1) {
// If XOR value is k
if (zor == k)
ans++;
return;
}
// Move rightwards
if (j + 1 < m)
backtracking(i, j + 1,
zor ^ mat[i, j + 1], mat);
// Move downwards
if (i + 1 < n)
backtracking(i + 1, j,
zor ^ mat[i + 1, j], mat);
}
// Function to calculate all possible paths
static int countPaths(int N, int M, int K, int [,]mat)
{
ans = 0;
n = N; m = M; k = K;
if (N == 1 && M == 1
&& mat[0, 0] == K) {
return 1;
}
// Calling the backtracking function
backtracking(0, 0, mat[0, 0], mat);
return ans;
}
// Driver Code
public static void Main()
{
int N = 3, M = 3, K = 11;
int [,]mat = { {2, 1, 5}, {7, 10, 0},
{12, 6, 4} };
// Function call
int ans = countPaths(N, M, K, mat);
Console.Write(ans+ "\n");
}
}
// This code is contributed by Samim Hossain Mondal.
C++14
// C++ code to implement the above approach
#include
using namespace std;
#define ll long long int
ll n, m, k;
vector> a;
ll cnt1 = 0, cnt2 = 0;
map >, ll> mp1, mp2;
// Backtracking function for the left part
void part1(ll i, ll j, ll cnt, ll zor)
{
if (cnt <= 0) {
// Count the number of triplets
mp1[{ zor, { i, j } }]++;
return;
}
// Move rightwards
if (j + 1 < m) {
part1(i, j + 1, cnt - 1,
zor ^ a[i][j + 1]);
}
// Move downwards
if (i + 1 < n)
part1(i + 1, j, cnt - 1,
zor ^ a[i + 1][j]);
}
// Backtracking function for the right part
void part2(ll i, ll j, ll cnt, ll zor)
{
if (cnt <= 0) {
// Count the number of triplets
mp2[{ zor, { i, j } }]++;
return;
}
// Move leftwards
if (j - 1 >= 0) {
part2(i, j - 1, cnt - 1,
zor ^ a[i][j - 1]);
}
// Move upwards
if (i - 1 >= 0)
part2(i - 1, j, cnt - 1,
zor ^ a[i - 1][j]);
}
// Function to count the paths with xor K
int countPaths(int N, int M, int K)
{
ll ans = 0;
n = N; m = M; k = K;
// Number of steps for left part
cnt1 = (n + m - 2) / 2;
// NUmber of steps for right part
cnt2 = (n + m - 2) - cnt1;
// number of steps in both parts are 0
if (n == 1 && m == 1 && a[0][0] == k) {
return 1;
}
// Calling the recursive function
// for the left and right part
part1(0, 0, cnt1, a[0][0]);
part2(n - 1, m - 1, cnt2 - 1,
a[n - 1][m - 1]);
// mp2 contains triplet of right part so
// traverse the triplets of right part
for (auto i : mp2) {
// Extracting all elements
// from the triplet
ll zor = i.first.first;
ll idx = i.first.second.first;
ll j = i.first.second.second;
ll cnt = i.second;
// XOR OF RIGHT SIDE IS zor ,
// then Xor of left side
// must be zor^k such that Xor
// of the total path is K
ll required = k ^ zor;
// Checking if one index to the left
// are left triplet as how many paths
if ((idx - 1) >= 0
&& mp1.find({ required,
{ idx - 1, j } })
!= mp1.end()) {
// Total path would be paths
// in left * paths in right
ans += (cnt
* mp1[{ required,
{ idx - 1, j } }]);
}
// Checking if one index upwards
// are left triplet as how many paths
if ((j - 1) >= 0
&& mp1.find({ required,
{ idx, j - 1 } })
!= mp1.end()) {
// Total path would be paths
// in left * paths in right
ans += (cnt
* mp1[{ required,
{ idx, j - 1 } }]);
}
}
return ans;
}
// Driver Code
int main()
{
int N = 3, M = 3, K = 11;
a = { {2, 1, 5}, {7, 10, 0},
{12, 6, 4} };
int ans = countPaths(N, M, K);
cout << ans;
return 0;
}
3
时间复杂度: O(2 X * X) 其中 X = (N + M – 2)
辅助空间: O(1)
为什么简单地回溯会在这里失败?
从 ( 1, 1) 移动到 (N, M)所需的移动次数将等于(N+M-2) 。现在,使用递归回溯解决方案将花费O(2 (N+M-2) )时间来迭代此长度的所有路径并检查每个路径是否具有等于K的 XOR。对于(N+M)的较高值,该算法变得非常耗时,因为该值接近此处提供的约束。
高效方法:使用Meet in The Middle 算法分割步数,使(N + M – 2)/2 在前半部分,在下半部分休息。请按照以下步骤解决问题:
- 并将两种解法的答案结合起来,基本就是Meet in Middle Algorithm的标准思路。
- 将这个 n+m-2 位的掩码分成两部分, mid = (n+m-2)/2位和(n+m-2)-mid位。
- 对于左侧部分:
- 从单元格(1,1)到底部执行递归回溯,并在中间步骤中保持路径的异或。在中间步骤之后,我们有索引位置即 (i,j) 和到该点的 xor 值,并计算有多少这样的三元组退出{zor, i,j}
- 对于正确的部分:
- 从单元格(N,M)开始向左或向上执行递归回溯,并保持路径的异或
(N+M-2) – 中间 -1 步。
- 从单元格(N,M)开始向左或向上执行递归回溯,并保持路径的异或
- 在中间步骤之后,找到索引位置即(i,j)和到该点的异或值,并计算有多少这样的三元组退出{zor,i,j}
- 在为这两个部分编写代码之后,现在遍历第二部分的三元组并检查是否在一个步骤中,即 (i-1,j) 或 (j-1, i) 我们在左侧的三元组中有一个具有 xor K的三元组。
下面是上述方法的实现:
C++14
// C++ code to implement the above approach
#include
using namespace std;
#define ll long long int
ll n, m, k;
vector> a;
ll cnt1 = 0, cnt2 = 0;
map >, ll> mp1, mp2;
// Backtracking function for the left part
void part1(ll i, ll j, ll cnt, ll zor)
{
if (cnt <= 0) {
// Count the number of triplets
mp1[{ zor, { i, j } }]++;
return;
}
// Move rightwards
if (j + 1 < m) {
part1(i, j + 1, cnt - 1,
zor ^ a[i][j + 1]);
}
// Move downwards
if (i + 1 < n)
part1(i + 1, j, cnt - 1,
zor ^ a[i + 1][j]);
}
// Backtracking function for the right part
void part2(ll i, ll j, ll cnt, ll zor)
{
if (cnt <= 0) {
// Count the number of triplets
mp2[{ zor, { i, j } }]++;
return;
}
// Move leftwards
if (j - 1 >= 0) {
part2(i, j - 1, cnt - 1,
zor ^ a[i][j - 1]);
}
// Move upwards
if (i - 1 >= 0)
part2(i - 1, j, cnt - 1,
zor ^ a[i - 1][j]);
}
// Function to count the paths with xor K
int countPaths(int N, int M, int K)
{
ll ans = 0;
n = N; m = M; k = K;
// Number of steps for left part
cnt1 = (n + m - 2) / 2;
// NUmber of steps for right part
cnt2 = (n + m - 2) - cnt1;
// number of steps in both parts are 0
if (n == 1 && m == 1 && a[0][0] == k) {
return 1;
}
// Calling the recursive function
// for the left and right part
part1(0, 0, cnt1, a[0][0]);
part2(n - 1, m - 1, cnt2 - 1,
a[n - 1][m - 1]);
// mp2 contains triplet of right part so
// traverse the triplets of right part
for (auto i : mp2) {
// Extracting all elements
// from the triplet
ll zor = i.first.first;
ll idx = i.first.second.first;
ll j = i.first.second.second;
ll cnt = i.second;
// XOR OF RIGHT SIDE IS zor ,
// then Xor of left side
// must be zor^k such that Xor
// of the total path is K
ll required = k ^ zor;
// Checking if one index to the left
// are left triplet as how many paths
if ((idx - 1) >= 0
&& mp1.find({ required,
{ idx - 1, j } })
!= mp1.end()) {
// Total path would be paths
// in left * paths in right
ans += (cnt
* mp1[{ required,
{ idx - 1, j } }]);
}
// Checking if one index upwards
// are left triplet as how many paths
if ((j - 1) >= 0
&& mp1.find({ required,
{ idx, j - 1 } })
!= mp1.end()) {
// Total path would be paths
// in left * paths in right
ans += (cnt
* mp1[{ required,
{ idx, j - 1 } }]);
}
}
return ans;
}
// Driver Code
int main()
{
int N = 3, M = 3, K = 11;
a = { {2, 1, 5}, {7, 10, 0},
{12, 6, 4} };
int ans = countPaths(N, M, K);
cout << ans;
return 0;
}
3
时间复杂度: O(X * 2 X ) 其中 X = (N + M – 2)/2
辅助空间: O(N + M)