掷硬币计数游戏C++数据结构!

Posted

技术标签:

【中文标题】掷硬币计数游戏C++数据结构!【英文标题】:Coin Flipping Counting Game C++ Data Structure! 【发布时间】:2019-11-26 12:45:04 【问题描述】:

问题来了。在游戏中,有一个长方形的硬币网格,正面=1,反面=0。游戏有一个简单的规则:玩家不能抛一枚硬币,而是可以选择一行(或一列)同时翻转该行(或该列)中的所有硬币。游戏的目标是找出一种翻转硬币的策略,以使正面硬币的数量最大化。第一个输入值是行 >> 然后是列 >> 和硬币

Sample inputs:
5 4
1010
0101
1010
1010
1010 //Sample output of this: 20
5 4
0010
1101
0110
0110
1011 //Sample output of this: 17

我用'0'和'1'的计数方法完成了我的代码,如果零更多,则切换它。这种方法只通过了简单的测试用例,但是当它进入困难的测试用例时,它失败了,因为有些情况需要多次推特。我想不出另一种更好的方法来处理它。

这是我的代码:

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

bool ConeIsMore(vector <vector<char> > table, int size, int j) 
    int countzero = 0;
    int countone = 0;
    for (int i = 0; i < size; i++) 
        (table[i][j] == '0')?++countzero: ++countone;
    
    if (countone >= countzero) 
        return true;
    
    return false;


bool RoneIsMore(vector <vector<char> > table, int size, int i) 
    int countzero = 0;
    int countone = 0;
    for (int j = 0; j < size; j++) 
        (table[i][j] == '0') ? ++countzero : ++countone;
    
    if (countone >= countzero) 
        return true;
    
    return false;


int main() 
    //Initialise row and column
    int row; 
    int column;
    while (cin >> row >> column) 
        //Initiallise 2D vector
        vector <vector<char> > table(row, vector<char>(column));

        //get each digit of number and store it into number
        for (int i = 0; i < row; i++) 
            for (int j = 0; j < column; j++) 
                cin >> table[i][j];
            
        

        //check for column
        for (int j = 0; j < column; j++) 
            if (!ConeIsMore(table, row, j)) 
                for (int i = 0; i < row; i++) 
                    (table[i][j] == '0') ? table[i][j] = '1' : table[i][j] = '0';
                
            
        

        //check for row
        for (int j = 0; j < row; j++) 
            if (!RoneIsMore(table, column, j)) 
                for (int i = 0; i < column; i++) 
                    (table[j][i] == '0') ? table[j][i] = '1' : table[j][i] = '0';
                
            
        

        //Count One in the table
        int ans = 0;
        for (int i = 0; i < row; i++) 
            for (int j = 0; j < column; j++) 
                (table[i][j] == '1') ? (ans++) : (ans = ans);
            
        
        cout << ans << endl;
    
    return 0;

当我研究测试用例时,我发现有一些需要检查各种时间,这让我觉得我的方法不是一个好方法。任何人都可以提出更好的处理方法吗?非常感谢。

以下是更难的测试用例:

5 4
0010
1101
0110
0110
1011 //17

5 4
0110
1111
0101
0110
0100  //16

5 4
0110
1001
0011
1110
1000 //16

5 4
1100
0001
1111
0101
1010 //16

5 4
0101
0110
1001
1000
0011 //16

5 4
0111
1100
0100
1000
1011 //16

5 4
1101
1110
0111
1011
0111 //15

5 4
1100
1001
0110
1001
1000 //17

【问题讨论】:

据我所知,这是一个算法问题,而不是一个编程问题。在完成算法之前,您不应该开始编写代码。请提供更难的测试用例 @ChristiePPP 作为旁注,我猜你误解了三元运算符的使用。 @Moia 是不是三元运算符指的是(语句)?(真表达式):(假表达式); @ThomasSablik 我最初认为的方法是为列切换一次,为行切换一次以获得结果,因为我得到的原始测试用例是我可以实现这种愚蠢方法的简单测试用例跨度> 我认为@Moia 指的是您使用三元运算符作为 if 语句,而忽略了它是一个实际返回值的表达式(您可以这样做;您的代码有效)。例如:(test) ? A = 1: A = 2;也可以写成A = (test) ? 1 : 2;,这样更符合运营商的预期用途。 【参考方案1】:

如果您翻转每一列或每一行,我们需要跟踪您的总数将有多少头。对于给定的行或列,如果您决定翻转该行或列,计算尾部的数量减去正面的数量以获得正面的净增加。将这些值存储在两个数组中,一个用于列,一个用于行。此时,您应该有两个数组,一个大小为行,一个大小为列,具有负值、零值或正值。

5 4
1010
0101
1010
1010
1010 //Sample output of this: 20

row: [0, 0, 0, 0, 0]
column: [-3, 3, -3, 3]

现在遍历行和列向量,如果遇到正值,则需要翻转该行或列。如果遇到负值,不要翻转。 如果您遇到零值,您的翻转决定应基于该行或列的第一个硬币是否已经是正面。这将有助于解决以下边缘情况:

2 2
10
01 // output should be: 4
row: [0, 0] // How can we know to flip row 1 but not row 0? Because arr[0][0] = 1 already
column: [0, 0]

您必须采取的另一个步骤是,当您翻转一行时,您也必须更新列数组中的值,反之亦然。您还应该更新内存中的二维硬币数组以反映新状态。第一次翻转后,问题状态看起来像这样

5 4
1010
1010 // this row was flipped because arr[1][0] was 0
1010
1010
1010 

row: [0, 0, 0, 0, 0]
column: [-5, 5, -5, 5]

继续直到没有更多可以翻转的行或列。实现中有很好的递归机会。

【讨论】:

希望这个算法对你找到答案有帮助。由于这似乎与家庭作业有关,因此我将避免直接发布代码。如果解释不充分,可能是伪代码。 非常感谢。我正在尝试实施 我已经实现了代码,但它似乎不起作用。我在下面发布了我的代码作为答案。你能帮忙检查一下错误在哪里吗?非常感谢。 @ChristiePPP 当然。您没有定义 Flip 函数。我建议避免使用三元运算符,大多数时候你只需要一个 if 语句 @ChristiePPP 我已经修改了您的代码以使其正常工作。您可以将此作为参考。 repl.it/@PeterCheng2/Coin-Flip【参考方案2】:

您或许应该输出策略的结果,而不仅仅是正面数,以更好地了解正在发生的事情。改进实施的两个想法:

1) 当行或列中有偶数个硬币时,您的代码没有按照您描述的算法执行。你说:

计数'0'和'1',如果零多,则切换。

您的代码可以:

if (!ConeIsMore(table, row, j)) 
    // switch it

当没有更多的头时它会切换。 结果,当硬币数量为偶数时,您也可以在 正面和反面的数量相等。当计数相等时切换是推测性的,尚不清楚它是否可以改善任何东西,因此您可能应该特别对待它。

2) 你也许可以继续迭代,直到没有 列或行留在尾部多于头部。

由于数据结构std::vector&lt;bool&gt; table(row*column) 可能会更高效,但也需要更加小心地正确处理。

【讨论】:

【参考方案3】:

以下是在 Peter Cheng 提供算法的帮助下我的代码。它一直工作到计算行和列,但不能翻转表格。

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

int RcountDifference(vector <vector<char> > table, int col, int var) 
    int Difference = 0;
    for (int j = 0; j < col; j++) 
        if (table[var][j]=='1') 
            Difference -= 1;
        
        else 
            Difference += 1;
        
    
    return Difference;


int CcountDifference(vector <vector<char> > table, int row, int var) 
    int Difference = 0;
    for (int i = 0; i < row; i++) 
        if (table[i][var] == '1') 
            Difference -= 1;
        
        else 
            Difference += 1;
        
    
    return Difference;


int main() 
    //Initialise row and column
    int r;
    int c;
    while (cin >> r >> c) 
        //Initiallise 2D vector
        vector <vector<char> > table(r, vector<char>(c));

        //get each digit of number and store it into number
        for (int i = 0; i < r; i++) 
            for (int j = 0; j < c; j++) 
                cin >> table[i][j];
            
        

        //Build two arrays to store the value "zero-one"
        vector <int> row;
        vector <int> column;

        //store for vector
        //For Row
        for (int i = 0; i < r; i++) 
            row.push_back(RcountDifference(table, c, i));
        
        //For Column
        for (int j = 0; j < c; j++) 
            column.push_back(CcountDifference(table, r, j));
        

        Flip(r, c, table, row, column);
        for (int i = 0; i < r; i++) 
            for (int j = 0; j < c; j++) 
                //Flip for row
                if (row[i]>0) 
                    (table[i][j] == '0') ? '1' : '0';
                
                else if (row[i] == 0 && table[i][0]=='0') 
                    (table[i][j] == '0') ? '1' : '0';
                
                else 
                    (table[i][j] == '0') ? '0' : '1';
                
                row.clear();
                row.push_back(RcountDifference(table, c, i));
                column.clear();
                column.push_back(CcountDifference(table, r, j));
                //Flip for column
                if (column[j] > 0) 
                    (table[i][j] == '0') ? '1' : '0';
                
                else if (column[j] == 0 && table[0][j] == '0') 
                    (table[i][j] == '0') ? '1' : '0';
                
                else 
                    (table[i][j] == '0') ? '0' : '1';
                
                row.clear();
                row.push_back(RcountDifference(table, c, i));
                column.clear();
                column.push_back(CcountDifference(table, r, j));
            
        

        //Output Check
        for (int i = 0; i < r; i++) 
            for (int j = 0; j < c; j++) 
                cout << table[i][j];
            
        

    

    return 0;

在我完成我的解决方案后,我也想把它放在这里,对于像我这样不熟悉编码的人来说,学习不同的问题会很好。

【讨论】:

你告诉我的递归,我不知道如何用二维向量实现。但是由于连翻转都不能成功,所以我没有做递归。@Peter Cheng​​span> 【参考方案4】:

对不起,@Peter Cheng 的回答在某些测试用例中可能不正确。只需用他的算法试试这个:

1 0 1 0
1 1 0 1
1 0 0 1
1 1 1 1
1 1 1 0

C[-5,-1,-1,-1]

R[0,-2,0,-4,-2]

使用他的算法,答案将是 14。但是,您可以翻转第三行,然后翻转第四列以获得 15 个正面。

我建议测试这两种情况。如果您遇到零值,您的翻转决定应基于该行或列的第一个硬币是否已经是正面。而且,如果您遇到零值,您的翻转决定应基于该行或列的第一个硬币是否不是正面,然后取较高的值作为最终答案。

【讨论】:

以上是关于掷硬币计数游戏C++数据结构!的主要内容,如果未能解决你的问题,请参考以下文章

期望最大化抛硬币示例

Redis LFU 实现 -- 掷硬币的艺术

R掷硬币500次,出现真面朝上的概率

如何防止用户堵塞我的扫描仪?

LeetCode1561. 你可以获得的最大硬币数目(C++)

LeetCode1561. 你可以获得的最大硬币数目(C++)