目錄
1、上樓梯
有個小孩正在上樓梯,樓梯有n階臺階,小孩一次可以上1階、2階、3階。請實現一個方法,計算小孩有多少種上樓的方式。爲了防止溢出,請將結果Mod 1000000007
給定一個正整數int n,請返回一個數,代表上樓的方式數。保證n小於等於100000。
測試樣例1:
1
返回:1
測試樣例2:
3
返回:4
測試樣例3:
4
返回:7
/*
思路:上1級有一種,2級上1級後再上1級,3級有上1級之後上2級或上2級之後上1級或直接上3級
4級有1級上3級,2級上2級,3級上1級
總結起來有:f(1)=1,f(2)=2,f(3)=4,f(4)=f(1)+f(2)+f(3);f(5)=f(2)+f(3)+f(4)
f(n)=f(n-1)+f(n-2)+f(n-3);
本來使用遞歸做的,結果遞歸做到最後超時了,只好用迭代了;
*/
public static int countWays(int n) {
if(n<1) return 0;
if(n==1) return 1;
if(n==2) return 2;
if(n==3) return 4;
int array[] =new int[n];
array[0] =1;
array[1] =2;
array[2] =4;
for(int i=3;i<n;i++){
array[i]=((array[i-1]+array[i-2])%1000000007+array[i-3]%1000000007)%1000000007;
}
return array[n-1];
}
2、機器人走方格I
第一種解法:
由於題目中x+y<=12,所以不必擔心遞歸超時問題,對於每一步,只要沒有走到
邊緣(x==1||y==1)就會有兩種選擇:向下走(x-1)or 向右走(y-1),終
止條件即走到邊緣,只能一直走下去,即此時只有一種走法。
public int countWays(int x, int y) {
if(x<1||y<1){
return 0;
}
if(x==1||y==1){
return 1;
}
return countWays(x-1,y)+countWays(x,y-1);
}
第二種解法:
基本動態規劃
public int countWays(int x, int y) {
int[][] dp = new int[x][y];
dp[0][0] = 1;
for(int i = 1; i < x; i++){
dp[i][0] = dp[i-1][0];
}
for(int i = 1; i < y; i++){
dp[0][i] = dp[0][i-1];
}
for(int i = 1; i < x; i++){
for(int j = 1; j < y; j++){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[x-1][y-1];
}
第三種解法:
考慮原始DP問題爲二維數組表示當前位置的步數,顯然有: dp[i][j] = dp[i-1][j] + dp[i][j-1],
而容易發現dp數組可以壓縮爲1維存儲空間dp[y]。原dp[i-1][j]爲上一行當前列的步數,
而現dp[j]就存儲了上一次該列的步數,而現dp[j-1]爲左節點的步數即原dp[i][j-1]。
所以變爲:dp[j] = dp[j] + dp[j-1].
public int countWays(int x, int y) {
// write code here
int dp[] =new int[y];
for(int i = 0; i < y; i++)
dp[i] = 1;
for(int i = 1; i < x; i++){
for(int j = 1; j< y; j++){
dp[j] = dp[j] + dp[j-1];
}
}
return dp[y-1];
}
3、機器人走方格II
/*
* 1.判斷右下角的點以及起點自身是否爲障礙點,若是返回0;
* 2.若右下角的點非障礙點,判斷上面和左邊是否爲障礙點
* 1.若全都爲障礙點,返回0
* 2.若一個爲障礙點,一個不是,則到該點的路徑數等於上一個點的路徑數(這是遞歸的思路)
* 第2部分可以不用遞歸,而用循環:
* dp[i-1][j-1]表示從(0,0)到(i,j)的方法數,如果(i,j)非1,則爲障礙點,對應dp[i-1][j-1]爲0
* 其餘情況與一般dp相同
*/
public int countWays(int[][] map, int x, int y) {
if(map == null || map.length != x || map[0].length != y){
return 0;
}
if(map[x-1][y-1] != 1 || map[0][0] != 1){//最後一個點爲障礙點
return 0;
}
int dp[][] = new int[x][y];
dp[0][0] = 1;
for(int i = 1; i < x; i++){
if(map[i][0] != 1){
dp[i][0] = 0;
}else{
dp[i][0] = dp[i-1][0];
}
}
for(int i = 1; i < y; i++){
if(map[0][i] != 1){
dp[0][i] = 0;
}else{
dp[0][i] = dp[0][i-1];
}
}
for(int i = 1; i < x; i++){
for(int j = 1; j < y; j++){
if(map[i][j] != 1){
dp[i][j] = 0;
}else{
dp[i][j] = dp[i-1][j]%1000000007 + dp[i][j-1]%1000000007;
}
}
}
return (dp[x-1][y-1]%1000000007);
}
4、魔術索引 II
在數組A[0..n-1]中,有所謂的魔術索引,滿足條件A[i]=i。給定一個不下降序列,元素值可能相同,編寫一個方法,判斷在數組A中是否存在魔術索引。請思考一種複雜度優於o(n)的方法。
給定一個int數組A和int n代表數組大小,請返回一個bool,代表是否存在魔術索引。
測試樣例:
[1,1,3,4,5]
返回:true
二分法
public boolean findMagicIndex(int[] A, int n) {
return findMagic(A,0,n-1);
}
private boolean findMagic(int[] A,int start,int end){
int mid=(start+end)>>1;
if(start>end){
return false;
}
if(A[start]==start||A[end]==end||A[mid]==mid){
return true;
}
return findMagic(A,start+1,mid)||findMagic(A,mid+1,end-1);
}