1. N-Queens
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q'
and '.'
both indicate a queen and an empty space respectively.
Example:
Input: 4 Output: [ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ] Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.
思路
尝试以回溯来解题,结果还是做不下去。分析下别人的答案,果然回溯是可以解这道题的。回溯的思路不对,把数独那题的解法照搬到这题上来了,事实上的确有相似的部分,但是完全照搬果然是不行的。
自己用回溯来解的时候发现两个比较麻烦的问题,一是这题要求解的是所有可能的组合,即正确的组合可能有多个,其次是如何判断两个斜对角线上的问题把我搞晕了,算法真的好难,好难,好难。
代码:
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> res=new ArrayList();
char[][] chessboard=new char[n][n];
for(int i=0;i<n;i++){ // 这里曾尝试用foreach来赋初始值,结果不对
for(int j=0;j<n;j++){
chessboard[i][j]='.';
}
}
solve(res, chessboard, 0, n);
return res;
}
void solve(List<List<String>> res, char[][] chessboard, int row, int n){
if(row==n){
List<String> ls=new ArrayList();
for(int i=0; i<n; i++){
ls.add(new String(chessboard[i]));
}
res.add(ls);
return;
}
for(int col=0; col<n; col++){
if(isValid(chessboard, row, col, n)){
chessboard[row][col]='Q';
solve(res, chessboard, row+1, n); // 逐行放置确保行合法,不需要再作额外判断
chessboard[row][col]='.';
}
}
}
boolean isValid(char[][] chessboard, int row, int col, int n){
for(int i=0;i<row;i++){ // 判断当前列有没有Q,注意这里只需要判断当前行之前的列那部分就可以了,不需要全部判断!
if(chessboard[i][col]=='Q') return false;
}
for(int i=row-1, j=col-1; i>=0 && j>=0;i--,j--){ // 45度斜对角线,和上面一样只需要判断之前的部分
if(chessboard[i][j]=='Q') return false;
}
for(int i=row-1, j=col+1; i>=0 && j<n;i--,j++){ // 135度对角线
if(chessboard[i][j]=='Q') return false;
}
return true;
}
}
要注意的上面那个isValid判断,每次判断合法不是一次性判断整行整列,而是和它之前确定的部分判断,只要放置之前不和之前放置的冲突就能确保每次放置后的棋盘是合法的。
2. Edit Distance
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
- Insert a character
- Delete a character
- Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros" Output: 3 Explanation: horse -> rorse (replace 'h' with 'r') rorse -> rose (remove 'r') rose -> ros (remove 'e')
Example 2:
Input: word1 = "intention", word2 = "execution" Output: 5 Explanation: intention -> inention (remove 't') inention -> enention (replace 'i' with 'e') enention -> exention (replace 'n' with 'x') exention -> exection (replace 'n' with 'c') exection -> execution (insert 'u')
思路
又是一道dp可以求解的题目,然而又不会建dp模型。
f(i, j) = minimum cost (or steps) required to convert first i characters of word1 to first j characters of word2
Case 1: word1[i] == word2[j], i.e. the ith the jth character matches.
f(i, j) = f(i - 1, j - 1)
Case 2: word1[i] != word2[j], then we must either insert, delete or replace, whichever is cheaper
f(i, j) = 1 + min { f(i, j - 1), f(i - 1, j), f(i - 1, j - 1) }
- f(i, j - 1) represents insert operation
- f(i - 1, j) represents delete operation
- f(i - 1, j - 1) represents replace operation
Here, we consider any operation from word1 to word2. It means, when we say insert operation, we insert a new character after word1 that matches the jth character of word2. So, now have to match i characters of word1 to j - 1 characters of word2. Same goes for other 2 operations as well.
Note that the problem is symmetric. The insert operation in one direction (i.e. from word1 to word2) is same as delete operation in other. So, we could choose any direction.
Above equations become the recursive definitions for DP.
Base Case:
f(0, k) = f(k, 0) = k
没什么好说的了,还是慢慢累积dp经验吧。不过现在有点感觉是,和之前那个字符串模式串匹配的dp类似,这种类字符串之间匹配,转化的问题在考虑子问题之间的联系(求地推公式)时,都是从字符串的最右边字符开始考虑,现在想起来求公共最长字串的dp好像也是这样,考虑子问题中两个字符串的最右边字符是否相等的情况,然后就得出了地推公式......,不知道这能否能通用起来。
代码:
class Solution {
public int minDistance(String word1, String word2) {
int[][] dp=new int[word1.length()+1][word2.length()+1];
for(int i=0;i<=word2.length();i++){
dp[0][i]=i;
}
for(int i=0;i<=word1.length();i++){
dp[i][0]=i;
}
for(int i=1;i<=word1.length();i++){
for(int j=1;j<=word2.length();j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
int min=Math.min(dp[i][j-1], dp[i-1][j]);
dp[i][j]=Math.min(min+1, dp[i-1][j-1]+1);
}
}
}
return dp[word1.length()][word2.length()];
}
}