📌  相关文章
📜  按位异或等于 K 的给定矩阵的可能路径计数

📅  最后修改于: 2022-05-13 01:56:10.327000             🧑  作者: Mango

按位异或等于 K 的给定矩阵的可能路径计数

给定一个N*M (N + M ≤ 40)矩阵mat[][] ,每个单元格都有一些从0 到 10 18的值和一个整数K 。查找所有可能路径的计数,使得路径中元素的按位 XOR 等于K。仅允许向右和向下移动。

例子:

朴素方法:简单的方法是遍历所有可能的路径并检查该路径的 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 步。
  • 中间步骤之后,找到索引位置即(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)