算法问题:翻转列

时间:2011-08-19 02:46:39

标签: algorithm optimization binary grid

假设我们给出了一个m×n的0和1网格,并且想要转换网格,以便最大行数仅由1组成。我们允许在网格上执行的唯一操作是选择一些列并翻转该列中的所有零和一个。我们还给出了一些整数k,并且必须执行完全 k列翻转。给定网格和k的值,我们如何确定要翻转哪些列以最大化所有列的行数?

我认为需要做一些动态的事情,但我无法得到一个好的答案。有人可以帮忙吗?

4 个答案:

答案 0 :(得分:17)

让我们首先考虑问题的一个重要细节:如果两行包含不同行的列(例如,在一行中它是零,在一行中它是一行),那么就没有可能将两行都放在一起的方法。要看到这一点,假设行x在某些列中为零,而行y在该列中有一个。然后,如果我们不翻转该列,则行x不能全部为1,如果我们翻转列,则行y不能全部为1。因此,如果我们看一下原始矩阵并尝试考虑我们将要创建所有行的行,我们基本上只是选择一组彼此相等的行。我们的目标是选择尽可能大的相同行集,并且可以使用恰好k个翻转来制作所有行。假设可以使用恰好k翻转的所有行进行的行是“候选行”。然后我们只需要在矩阵中找到最多次出现的候选行。

执行此操作的实际算法取决于是否允许我们两次翻转同一列。如果我们不是,则候选行是其中恰好有k个零的行。如果我们可以多次翻转同一列,那么这有点棘手。要使行全部为1,我们需要将每个零转换为1,然后需要继续花费剩余的翻转两次翻转同一列以避免将任何一个更改为零。如果k与行中的零数之间的差值是非负偶数,则为真。

使用这个,我们得到以下算法:

  1. 维护从候选行到其频率的哈希表映射。
  2. 每行:
    1. 计算行中的数字或零。
    2. 如果k与此数字之间的差值是非负偶数,请通过增加此特定行的频率来更新哈希表。
  3. 查找总频率最高的哈希表中的候选行。
  4. 输出该行的频率。
  5. 总的来说,在m×n矩阵(m行,n列)上,我们访问每一行一次。在访问期间,我们做O(n)工作来计算零的数量,然后O(1)工作以查看它是否有效。如果是这样,则需要预期的O(n)时间来更新散列表,因为散列步骤花费O(n)时间来散列该行。这意味着我们将O(mn)时间填入表中。最后,最后一步需要时间O(m)来找到最大频率行,对于O(mn)的净运行时间,其在输入的大小上是线性的。而且,这是渐近最优的,因为如果我们花费的时间少于此,我们就无法看到矩阵元素al!

    希望这有帮助!这是一个很酷的问题!

答案 1 :(得分:2)

这可能是代码:

int candidateRowCount=0;
void populateHash(int *input, int **hash, int col)
{
    bool found = false;
    for (int i=0; i<candidateRowCount; i++)
    {
        for (int j=0; j<col; j++)
        {
            if(input[j] !=hash[i][j])
                break;
            else
            {
                if (j==(col-1))
                {
                    found = true;
                    hash[i][col] +=1;
                }
            }
        }
    }

    if (!found)
    {   // save current candidate Row  in hash to be used for comparison in else block of above steps of this function 
        for (int i=0; i<col; i++)
        {
            hash[candidateRowCount][i] = input[i];
        }
        hash[candidateRowCount][col] +=1;
        candidateRowCount++;
    }
}

int getMaxCount(int** input, int N , int M, int K)
{
    int **hash= new int *[N];
    // an extra column to save frequency of row i
    for(int i=0; i<M+1; i++)
    {
        hash[i]= new int[M+1];
        hash[i][M] =0;
    }
    for(int i=0; i<N; i++)
        for(int j=0; i<M; j++)
            hash[i][j]=0;

    for(int i=0; i<N; i++)
    {
        int count = 0;
        for (int j=0; j<M; j++)
        {
            if(input[i][j]==0)
                count++;
        }
        if(count<=K)
        {
            int diff= K-count;
            if ((diff >= 0) && ((diff %2)== 0))
            {
                populateHash(input[i], hash, M);
            }
        }
    }

    int maxVal = 0;
    for (int i=0; i<candidateRowCount; i++)
    {
        if(hash[i][M]>maxVal)
            maxVal = hash[i][M];
    }
    return maxVal;
}

int main()
{
    freopen("input.txt", "r", stdin);
    int testcase;
    cin >> testcase;
    for (int t=0; t<testcase; t++)
    {
        int N,M,K;
        cin >> N >>M;
        cin >>K;
        int **input = new int *[N];
        for (int i=0; i<N; i++)
            input[i] = new int [M];
        for (int i=0; i<N; i++)
        {
            for (int j=0; j<M; j++)
            {
                cin >> input[i][j];
            }
        }
        int val= getMaxCount(input, N, M, K);

        cout << "Max Val" <<val << endl;
        candidateRowCount= 0;
    }
    return 0;
}

答案 2 :(得分:0)

如果有人告诉你顶行必须完全由1组成,那么你需要翻转顶部有0的所有列。同样,对于每一行,您都可以计算出使该行全部为1所必需的列翻转模式。

因此,为每一行计算所需的列翻转模式,并找出最常出现的列翻转模式,例如:通过在哈希表中存储计数或在模式上运行排序/合并(好的,更昂贵,但如果你计划进行动态编程,我认为你可以负担得起)。

答案 3 :(得分:0)

#include <iostream>
using namespace std;
// Solved in O(n*m)
int count_normal(int **mat, int n, int m)
{
    int count = 0;
    for (int i = 0; i < n; i++)
    {
        int tempCount = 0;
        for (int j = 0; j < m; j++)
            tempCount += mat[i][j];
        if (tempCount == m)
            count += 1;
    }
    return count;
}

int *rowWiseSum(int **mat, int n, int m)
{
    int *rowwisesum = new int[n];
    for (int i = 0; i < n; i++)
    {
        int tempSum = 0;
        for (int j = 0; j < m; j++)
            tempSum += mat[i][j];
        rowwisesum[i] = tempSum;
    }
    return rowwisesum;
}

int count_toggled(int *rowwisesum, int **mat, int toggle, int n, int m)
{
    int count = 0;
    for(int i=0; i<n; i++){
        if(mat[i][toggle]==1){
            rowwisesum[i]-=1;
        }else{
            rowwisesum[i]+=1;
        }
    }
    for(int i=0; i<n; i++){
        if(rowwisesum[i]==m)
            count+=1;
    }

    //resetting rowwisesum
    for (int i = 0; i < n; i++){
        if (mat[i][toggle] == 1){
            rowwisesum[i] += 1;
        }else{
            rowwisesum[i] -= 1;
        }
    }
    return count;
}

int main()
{
    int n, m, k;
    cout << "Enter the value of N : ";
    cin >> n;
    cout << "Enter the value of M : ";
    cin >> m;
    cout << "Enter the value of k : ";
    cin >> k;

    int **mat = new int *[n];
    for (int i = 0; i < n; i++)
    {
        mat[i] = new int[m];
    }

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

    // for (int i = 0; i < n; i++){
    //     for (int j = 0; j < m; j++)
    //         cout << mat[i][j]<<" ";
    //     cout<<endl;
    // }
    int normalCount = count_normal(mat, n, m);
    if (k % 2 == 0)
    {
        cout << normalCount << endl;
    }
    else
    {
        int *rowwisesum = rowWiseSum(mat, n, m);
        int maxCount = normalCount, toggled = -1;
        for (int i = 0; i < m; i++)
        {
            int count = count_toggled(rowwisesum, mat, i, n, m);
            if (count > maxCount)
            {
                maxCount = count;
                toggled = i;
            }
        }
        cout << "\n\nMaxRowsWithAll1 = " << maxCount << " Upon toggling column " << toggled + 1 << ", " << k << " times" << endl;
    }

    return 0;
}