目录
- 题目
- 深搜(不超时)
- 动态规划(超时写法)
- 二维
- 空间优化(一维)
题目
给你一个整数 n
,如果你可以将 n
表示成若干个不同的三的幂之和,请你返回 true
,否则请返回 false
。
对于一个整数 y
,如果存在整数 x
满足 y == 3x
,我们称这个整数 y
是三的幂。
示例 1:
输入:n = 12 输出:true 解释:12 = 31 + 32
示例 2:
输入:n = 91 输出:true 解释:91 = 30 + 32 + 34
示例 3:
输入:n = 21 输出:false
提示:
1 <= n <= 107
深搜(不超时)
可以深度搜索的原因是,30,31 … 3n 是任意组合,只是每个只能选择一次,又因为107 < 316,所以数据量只有16个,是可以暴力的。
比如12,红色圈圈表示选或者不选
class Solution {
public boolean checkPowersOfThree(int n) {
return dfs(n, 0);
}
public boolean dfs(int target, int num) {
if (target == 0) return true;
if (target < 0) return false;
int count = num;
int ans = (int)Math.pow(3, count);
boolean temp = false;
while (ans <= target) {
temp |= dfs(target - ans, count + 1);
count++;
ans = (int)Math.pow(3, count);
}
return temp;
}
}
动态规划(超时写法)
思路跟01背包问题
一样,这个算法会超时。其实主要是练习一下动态规划的思想。
二维
根据 30,31 … 3m (这里可以用 w[i]表示每一个项)划分阶段,为了方便理解,状态可以先用dp[i][j]表示
,i是代表 30,31 ,3i … 3m,前i项是已经处理好的问题。j是1 <= n <= 107。状态转移是 j <= w[i] dp[i][j] = dp[i - 1][j] || j == wi;
j > wi时 dp[i][j] = dp[i - 1][j - w[i]];
class Sulution{
public boolean checkPowersOfThree(int n) {
int m = 0;
while (Math.pow(3, m) <= n) m++;
boolean[][] dp = new boolean[m][n + 1];
int[] w = new int[m];
for (int i = 0; i < m; i++) {
w[i] = (int)Math.pow(3, i);
}
dp[0][1] = true;
for (int i = 1; i < m; i++) {
for (int j = 1; j <= n; j++) {
if (j <= w[i]) {
dp[i][j] = dp[i - 1][j] || j == w[i];
} else {
dp[i][j] = dp[i - 1][j - w[i]];
}
}
}
return dp[m - 1][n];
}
}
空间优化(一维)
因为阶段之间是相隔的,既dp[i]
的解依赖与dp[i - 1]
的解,所以用一维数组就行了,时间复杂度跟二维一样,这里需要记录的是 第二层循环,需要从后面遍历。如果从前面遍历的话就是用dp[i]的解求dp[i]的解,就是完全背包问题
,我们这里是01背包问题
。
class Solution{
public boolean checkPowersOfThree(int n) {
boolean[] dp = new boolean[n + 1];
dp[0] = true;
int len = 0;
while (Math.pow(3, len) <= n) len++;
int[] w = new int[len];
for (int i = 0; i < len; ++i) w[i] = (int)Math.pow(3, i);
for (int i = 0; i < len; ++i) {
for (int j = n; j >= w[i]; j--) {
if ((j == w[i]) || (dp[j - w[i]] && j - w[i] != j)) {
dp[j] = true;
}
}
}
return dp[n];
}
}