今天刷题遇到一道特别奇葩的题,看起来是用动态规划解,题型分类也是动态规划,但是用动态规划确实TML,而最优解法感觉却有点像“脑筋急转弯”,I fulo U, 还是太菜了。
题目描述
我的解法:
1. 记忆化(TML)
vector<int> ans;
int l, s;
set<string> st;
void dfs(int k, int len)
{
string key = to_string(k) + "_" + to_string(len);
if (st.find(key) != st.end()) return;
if (k == 0)
{
st.insert(key);
ans.push_back(len);
return;
}
dfs(k - 1, len + l);
dfs(k - 1, len + s);
st.insert(key);
}
vector<int> divingBoard(int shorter, int longer, int k) {
if(k == 0) return {};
if(shorter == longer) return {k * longer};
l = longer;
s = shorter;
dfs(k, 0);
sort(ans.begin(), ans.end());
return ans;
}
3. 动态规划(TML)
vector<int> divingBoard(int shorter, int longer, int k) {
if(k <= 0) return {};
if(longer == shorter) return {k*longer};
vector<vector<int>> dp(k,vector<int>(k*longer+1,0));
dp[0][shorter] = 1;
dp[0][longer] = 1;
for(int i = 1; i < k; i++)
{
for(int j = 1; j < dp[i].size(); j++)
{
if(dp[i-1][j] == 1)
{
dp[i][j+shorter] = 1;
dp[i][j+longer] = 1;
}
}
}
vector<int> ans;
for(int i = 0; i < k*longer+1; i++)
{
if(dp[k-1][i]) ans.push_back(i);
}
return ans;
}
这里的动态规划有很多无效计算没有优化,因为优化后会发现其实和记忆化是一样的,这里就说下优化的思路吧
在遍历每个i对应的行时,我们可以不用遍历j: 1 ~ dp[i].size()
,只记录有用信息的状态,对应题目里就是dp[i][j]=1
的信息,我们每次把它包存在一个队列里面,然后遍历就只遍历队列的上个状态的长度,在遍历的过程中此次遍历的结果加入队尾即可。
3. 最优解法
思路:
-
当两种板子长度相同的时候,显然无论如何组合,跳板的长度均为 k*shorter。故只有一种长度。
-
当两种板子的长度不相同的时候,可以组合出 k+1 种长度的跳板,证码过程如下:
-
首先,我们选择k块短板,此时长度为 k*shorter。这是一种方案。
-
每用一块长板替换掉一块短板,跳板的长度就会增加 longer - shorter。故每替换一次,就会产生一种新方案。一共可以替换k次。故总共有 k + 1 种方案。
vector<int> divingBoard(int shorter, int longer, int k) {
if(k <= 0) return {};
if(shorter == longer) return vector<int>(1,k * shorter);
vector<int> ans;
for(int i = 0; i <= k; i++)
ans.push_back((k - i) * shorter + i * longer);
return ans;
}
这个解法我是看题解才知道的,之前全部时间都去推动态规划去了(哎!大神果然都不一般)。