Leetcode 293. Flip Game & 294. Flip Game II

来源:互联网 发布:灵格斯翻译家 for mac 编辑:程序博客网 时间:2024/05/24 02:08

293. Flip Game

Total Accepted: 14847 Total Submissions: 28238 Difficulty: Easy
  • Contributors: Admin

You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take 

turns to flip two consecutive "++" into "--". The game ends when a person can no longer make a move and therefore the other person will be 

the winner.

Write a function to compute all possible states of the string after one valid move.

For example, given s = "++++", after one move, it may become one of the following states:

[  "--++",  "+--+",  "++--"]

If there is no valid move, return an empty list [].

Hide Company Tags
 Google
Hide Tags
 String
Hide Similar Problems
 (M) Flip Game II

思路:

从左到右,直接重置,加入res,复原。

public class Solution {    public List<String> generatePossibleNextMoves(String s) {        List<String> res = new ArrayList<>();        char[] ss = s.toCharArray();        for(int i = 0; i < ss.length - 1; i++){            if(ss[i] == '+' && ss[i + 1] == '+'){                ss[i] = '-';                ss[i + 1] = '-';                res.add(String.valueOf(ss));                ss[i + 1] = '+';                ss[i] = '+';            }        }        return res;    }}

294. Flip Game II

Total Accepted: 15396 Total Submissions: 35004 Difficulty: Medium
  • Contributors: Admin

You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend

 take turns to flip two consecutive "++" into "--". The game ends when a person can no longer make a move and therefore the other person

will be the winner.

Write a function to determine if the starting player can guarantee a win.

For example, given s = "++++", return true. The starting player can guarantee a win by flipping the middle "++" to become "+--+".

Follow up:
Derive your algorithm's runtime complexity.

Hide Company Tags
 Google
Hide Tags
 Backtracking
Hide Similar Problems
 (E) Nim Game (E) Flip Game (M) Guess Number Higher or Lower II

思路:

创历史了,居然被backtracking折磨了3个多小时。一般DFS用于输出所有情况,然后如果是问能不能一般都用DFS+全局变量,DFS来更新全局变量。


这个题非常优秀,看似DFS,但根本不是dfs。因为问的是能不能保证第一个人赢,对于第一个人,只要他能有一个方法赢就行;而对于第二个人而言,必须第二个人所有方法都无法阻止第一个人赢。


这个题其实非常的巧妙,它是问你能不能,但是并不能用DFS加全局变量,而且DFS函数要区别对待。平时我们写树的递归,每个节点和每个节点没差别,然而这个题是问我们第一个人能不能赢,也就是说我们DFS结果返回的true和false要根据当前选手而定,而且是为第一个选手做出的返回值。


先上个非常容易理解的版本,来自这里:

如果能找到任何一种方式,使得之后第二个选手无法赢,那么表明第一个选手可以赢。

public class Solution {    public boolean canWin(String s) {        char[] arr = s.toCharArray();        for(int i = 1; i < s.length(); i++) {            if(arr[i] == '+' && arr[i - 1] == '+') {                arr[i] = '-';                arr[i - 1] = '-';                String next = String.valueOf(arr);                if(!canWin(next)) {                    return true;                }                arr[i] = '+';                arr[i - 1] = '+';            }        }                return false;    }}

一开始就用DFS,后来发现返回值应该根据不同player的人而异。具体思路应该如下:(此时函数其实是canPlayer1Win)

如果是第一个人的调用:只要找到一个情况保证第二个选手输就行,这时候 return true,否则loop结束返回false。

如果是第二个人的调用,对于自己的每一个可能的选择,都必须保证第一个选手能赢。

也就是说,第一个选手的round,自己可以做决定,选择能行的解;而别人(第二人)的round,必须让其所有选择都导致第一个人赢,否则无法保证第一个人必赢。


讨论区这里有个参考:

public class Solution {    public boolean canWin(String s) {        //remember bits flipped        int[] visited = new int[s.length()];        //mark existing - to 1        for(int i=0;i<s.length();i++){            if(s.charAt(i)=='-'){                visited[i]=1;            }        }        return canWinR(s,1,visited);    }        public boolean canWinR(String s, int step, int[] visited){        //first player's turn        if(step%2==1){            for(int i=0;i<s.length()-1;i++)            {                //if already flipped, skip                if(visited[i]==1||visited[i+1]==1) continue;                //mark the two to be flipped                    visited[i]=1;                    visited[i+1]=1;                    //if this move can win, return;                    boolean r=canWinR(s,step+1,visited);                    visited[i]=0;                    visited[i+1]=0;                    if(r) return true;            }            return false;                    } else {            //second player's turn.             //all second player's moves should result in first player to win            //otherwise, first player cannot be guaranteed to win            for(int i=0;i<s.length()-1;i++)            {                if(visited[i]==1||visited[i+1]==1) continue;                    visited[i]=1;                    visited[i+1]=1;                    boolean r = canWinR(s,step+1,visited);                    visited[i]=0;                    visited[i+1]=0;                    //this move leads to first player to lose , return false;                    if(!r) return false;            }            return true;        }    }}

其实他上面的代码是冗余的,直接用char[]就可以了(也就是其代码中的int[] visited):

public class Solution {    public boolean canWin(String s) {        //remember bits flipped        int[] visited = new int[s.length()];        //mark existing - to 1        for(int i=0;i<s.length();i++){            if(s.charAt(i)=='-'){                visited[i]=1;            }        }        return canWinR(1,visited);    }        public boolean canWinR(int step, int[] visited){        //first player's turn        if(step%2==1){            for(int i=0;i<visited.length-1;i++)            {                //if already flipped, skip                if(visited[i]==1||visited[i+1]==1) continue;                //mark the two to be flipped                    visited[i]=1;                    visited[i+1]=1;                    //if this move can win, return;                    boolean r=canWinR(step+1,visited);                    visited[i]=0;                    visited[i+1]=0;                    if(r) return true;            }            return false;                    } else {            //second player's turn.             //all second player's moves should result in first player to win            //otherwise, first player cannot be guaranteed to win            for(int i=0;i<visited.length-1;i++)            {                if(visited[i]==1||visited[i+1]==1) continue;                    visited[i]=1;                    visited[i+1]=1;                    boolean r = canWinR(step+1,visited);                    visited[i]=0;                    visited[i+1]=0;                    //this move leads to first player to lose , return false;                    if(!r) return false;            }            return true;        }    }}

博主本来被什么时候该返回什么搞的晕头转向,强行将逻辑改到一致:

public class Solution {    public boolean canWin(String s) {        //if(s == null || s.length() <= 1) return false;        char[] ss = s.toCharArray();        return canWin(ss, 1);    }        public boolean canWin(char[] ss, int player){        if(player == 1){            for(int i = 0; i < ss.length - 1; i++){                if(ss[i] == '+' && ss[i + 1] == '+'){                    ss[i] = '-';                    ss[i + 1] = '-';                    if(canWin(ss, 2)) return true;                    ss[i] = '+';                    ss[i + 1] = '+';                }            }            return false;        }else{            //second player's turn.             //all second player's moves should result in first player to win            //otherwise, first player cannot be guaranteed to win            for(int i = 0; i < ss.length - 1; i++){                if(ss[i] == '+' && ss[i + 1] == '+'){                    ss[i] = '-';                    ss[i + 1] = '-';                    if(!canWin(ss, 1)) return false;                     ss[i] = '+';                    ss[i + 1] = '+';                }            }            return true;        }    }}

以上代码在"++++"会return false,很明显是true。楼主以为是逻辑又错了,于是改了一个多小时。一会return false,一会true;一会canWin return;一会!canWin return。

当然都失败了,直到注意到一个特殊的细节:原代码中是先保存的了结果,复原之后判断的。看了下自己的代码输出,果然是出在复原问题上。改为如下终于AC了。


逻辑是如上所说,canWin其实的意思是canPlayer1Win!!!应该是第一次见到变种dfs+backtracking的题目。那么之前标准版满足return就得修改,得改成先保存结果再return。要不然当就会变成这样的运行模式:

++++ 1选手开始

--++ 到0选手上, 0将最后俩++变成--,----到1,return false,1要输,返回0的调用,因为我们已经发现这种情况会使得1输,已经没有必要继续1的--++这个选择。

然而因为立即return了,----并没有复原成--++,这样到了主循环本来应该复原成++++,就办不到了。


public class Solution {    public boolean canWin(String s) {        //if(s == null || s.length() <= 1) return false;        char[] ss = s.toCharArray();        return canWin(ss, 1);    }        public boolean canWin(char[] ss, int player){        if(player == 1){            for(int i = 0; i < ss.length - 1; i++){                if(ss[i] == '+' && ss[i + 1] == '+'){                    ss[i] = '-';                    ss[i + 1] = '-';                    boolean res = canWin(ss, 2);                    ss[i] = '+';                    ss[i + 1] = '+';                    if(res) return true;                }            }            return false;        }else{            //2 player's turn.             //all 2 player's moves should result in 1 player to win            //otherwise, 1 player cannot be guaranteed to win            for(int i = 0; i < ss.length - 1; i++){                if(ss[i] == '+' && ss[i + 1] == '+'){                    ss[i] = '-';                    ss[i + 1] = '-';                    boolean res = !canWin(ss, 1);                    ss[i] = '+';                    ss[i + 1] = '+';                    if(res) return false;                }            }            return true;        }    }}
                                             
0 0
原创粉丝点击