DP基本问题总结(2018.5.29 2018.6.9 2018.8.9更新)

本文解析了包括网易笔试合唱团问题在内的多个经典动态规划题目,涵盖了最长回文子串、爬楼梯、背包问题等多个典型场景,并提供了详细的代码实现及优化思路。

参考文献:

【1】https://segmentfault.com/a/1190000006325321

【2】https://blog.youkuaiyun.com/q623702748/article/details/51297949

一晃就过去了两个月,提前退出了网易游戏的实习,前途未卜。但不管怎么样,生活还是要继续。DP问题还是在笔试里考得多些,还要记录笔试DP的题目。

网易笔试:合唱团:https://www.nowcoder.com/practice/661c49118ca241909add3a11c96408c8?tpId=85&tqId=29830&tPage=1&rp=1&ru=/ta/2017test&qru=/ta/2017test/question-ranking

这个题目笔试确实超出了我现在能力范围了,就复习一下看个热闹吧。。

此题的思路是令dp[k][i]表示在前i个学生(必须以第i个学生结尾)中选k个让乘积最大,由于学生的能力值有正有负,我们需要同时维护两个数组,dp_max和dp_min,分别表示最大和最小乘积。

dp_max[k][i] = max(dp_max[k][i], max(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));

是说以第i个学生结尾选k个学生,即dp_max[k][i],它的更新公式是取k-1个学生,以之前符合条件的任意j为结尾,即

dp_max[k-1][j] 去乘第i个学生的能力值,或者dp_min[k-1][j] * arr[i],这三者取最大值。这里j是有范围限制的,即要<i,同时根据题意又不能间距超过d,故有条件i - j <= d。

ans = max(ans, dp_max[K][i])最后我们的答案是在i从1~N取值中遍历得到的。

原来特么的可以插入代码啊。。。我日昍晶

#include<iostream>
#include<vector>
using namespace std;
inline long long max(long long a, long long b) { return (a>b ? a : b); }
inline long long min(long long a, long long b) { return (a>b ? b : a); }
int main()
{
	int N; cin >> N;
	vector<long> arr; arr.push_back(0); int temp, count = 0;
	while (count < N  && cin >> temp) {
		arr.push_back(temp);
		count++;
	}
	int K; cin >> K; int d; cin >> d;
	vector<vector<long>> dp_max; 
	dp_max.resize(K + 1);
	for (int i = 0; i < K + 1; i++){
		dp_max[i].resize(N + 1);
	}
	vector<vector<long>> dp_min(dp_max); long ans = 0;
	for (int i = 1; i <= N; i++){
		dp_max[1][i] = arr[i]; dp_min[1][i] = arr[i];
		for (int k = 2; k <= K; k++)
		{
			for (int j = i - 1; j > 0 && i - j <= d; --j) {
				dp_max[k][i] = max(dp_max[k][i], max(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));
				dp_min[k][i] = min(dp_min[k][i], min(dp_max[k-1][j] * arr[i], dp_min[k-1][j] * arr[i]));
			}
		}
		ans = max(ans, dp_max[K][i]);
	}
	cout << ans << endl;
}

5. Longest Palindromic Substring

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

此题是比较经典的求最长回文串的题目,此题有一个比较有趣的思路是反转string s,找最长公共串,但是如果遇到例如“abcdgcba”这种自带部分回文的串就不行了,得到“abc”显然不对(有后续改进但我不想看了,有时间再补上)。

此题DP的的思路是设置dp[i][j]表示index i~j的子串是否是回文串,是为true否为false。

显然dp[i][i]均为true,同时我们可以根据串s来更新dp[i][i+1]的值:

dp(i,i+1)=(S​i​​==S​i+1​​)

初始值求完后,更新公式为:

dp(i,j)=(dp(i+1,j−1) and S​i​​==S​j​​)

这题目真的不难,奈何太久没做DP了,不会写。

This yields a straight forward DP solution, which we first initialize the one and two letters palindromes, and work our way up finding all three letters palindromes, and so on...

class Solution {
public:
    string longestPalindrome(string s) {
        string resErr;
        int len = s.size();
        if (len == 0)
            return resErr;
        vector<vector<int>> dp;
        dp.resize(len);
        for (int i = 0; i < len; i++) {
            dp[i].resize(len);
        }
        int begin = 0, end = 0;
        for (int i = 0; i < len; i++) {
            dp[i][i] = 1;
        }
        for (int i = 0; i < len - 1; i++) {
            if (s[i] == s[i + 1]) {
                dp[i][i + 1] = 1;
                begin = i; end = i + 1;
            }
        }
        for (int i = 2; i < len; i++) {
            for (int j = 0; j < len - i; j++) {
                if (s[j] == s[j + i] && dp[j + 1][j + i - 1] == 1) {
                    dp[j][j + i] = 1;
                    begin = j; end = j + i;
                }
            }
        }
        string res(s.substr(begin, end - begin + 1));
        return res;
    }
};

此篇由来:昨天(2018.5.28)被leetcode312那道hard难度的DP虐了以后,今天给了个基本题安慰了一下,所以顺手把一些DP的基本问题做过总结,水过今天的编程:) 首先看今天(2018.5.29)遇到的这一题:

70. Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

 

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

 

此题非常简单,既可以看作一个斐波那契数列,也可以当成最基础的DP问题(需要注意自顶向上计算的效率较高)。

dp[i]表示台阶数为i时的路径数,状态转移方程为dp[i] = dp[i-1] + dp[i-2],代码太简单了就不贴了。

LintCode 92.Backpack

 

Description

Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?

 

Example

If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], so that the max size we can fill this backpack is 10. If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack.

You function should return the max size we can fill in the given backpack.

Challenge

O(n x m) time and O(m) memory.

O(n x m) memory is also acceptable if you do not know how to optimize memory.

 

此题是一道最基本的背包问题,状态量dp[i][j]表示只允许拿前i个物品(0~i-1)中的任意个,背包容量为j时能达到的最大容量,很显然,状态转移方程为

以m=10,A=[2,3,5,7]为例,状态转移表如下所示,max那一项的含义是:对于第i个物品,我如果不拿,那么dp[i][j]的值还是dp[i-1][j]的值

代码如下,当时写的废话太多了:),代码的空间复杂度是O(nm),关于空间优化的问题请往下看。

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    int backPack(int m, vector<int> &A) {
        // write your code here
        vector<vector<int>> f;
        f.resize(A.size()+1);
        for(int i = 0;i < A.size()+1;i++)
        {
            f[i].resize(m+1);
        }
        for(int i = 0;i < A.size()+1;i++)
        {
            f[i][0] = 0;
        }
        for(int i = 0;i < m+1;i++)
        {
            f[0][i] = 0;
        }
        for(int i = 1;i < A.size()+1;i++)
        {
            for(int v = 1;v < m+1;v++)
            {
                if(v < A[i-1])
                   f[i][v] = f[i-1][v];
                else
                   f[i][v] = max(f[i-1][v],f[i-1][v-A[i-1]]+A[i-1]);
            }
        }
        return f[A.size()][m];
    }
};

 

 

LintCode 125.BackpackII

Description

Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?

Example

Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. The maximum value is 9.

Challenge

O(n x m) memory is acceptable, can you do it in O(m) memory?

这道题目是上一题的升级版,加入了每个物品的不同价值,但实际上两题没有任何区别_(¦3」∠)_,这里先给出此题的状态转移方程:

只是把上一题max中后一项加的A[i-1]换成了V[i-1],状态转移图这里也不再画了,这里讨论如何利用O(m)空间实现:

可以注意到在这两个题目中,当前计算的行之和上一行有关,这就有了优化的空间。然而当我们尝试只用一行去记录dp值时,会发现如果按照从左到右的顺序计算,前面计算的值会把后面的覆盖掉,例如在上一题的例子里,如果我们先更新了dp[1][4]为3,在更新dp[1][7]的时候,正确的值应为2+3(dp[1][4]+A[1]),但因为此时dp是一维向量,得到的值为3+3。对此解决方案是从右到左倒着更新

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        // write your code here
        vector<int> f;f.resize(m+1);
        for(int i = 0;i < A.size();i++)
        {
            for(int v = m;v >= A[i];v--)
            {
                f[v] = max(f[v],f[v-A[i]]+V[i]);
            }
        }
        return f[m];
        
    }
private:
    int max(int a,int b)
    {
        return (a>b)?a:b;
    }

};

 

LintCode 669.Coin Change

Description

:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

Example

Given coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

Given coins = [2], amount = 3
return -1.

最少硬币置换问题。设F[i]为置换面值i时最少的硬币数,这里的更新公式为:

(我真的是醉了,图片怎么都上传不上去,下次更新我再试试微笑,6.9注:今天还是生病状态,拖到下次再弄这里吧......)

注意我们应该讲F[0]设置为0,其余初始值设置为-2(或者某个标记数),让循环能够正常开始,以Example为例,F值的更新如下图所示:

0 1 2 3 4 5 6 7 8 9 10 11
1 0 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

5 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2

 

0 1 2 3 4 5 6 7 8 9 10 11
1 0 1 2 3 4 5 6 7 8 9 10 11
2 0 1 1 2 2 3 3 4 4 5 5 6

 

5 0 1 1 2 2 1 2 2 3 3 2 3

此题的testcase里会出现硬币面值为0的情况,这点需要主要。

class Solution {
public:
    /**
     * @param coins: a list of integer
     * @param amount: a total amount of money amount
     * @return: the fewest number of coins that you need to make up
     */
    int coinChange(vector<int> &coins, int amount) {
        // write your code here
        vector<int> f(amount + 1, -2); f[0] = 0;
sort(coins.begin(), coins.end());
coins.erase(remove(coins.begin(), coins.end(), 0), coins.end());
for (int i = 0; i < coins.size(); i++)
{
for (int v = 0; v < amount + 1; v++)
{
if (v >= coins[i])
{
if (f[v - coins[i]] == -2)
{
if (f[v] == -2)
f[v] = -2;
else
;
}
else
{
if (f[v] == -2)
f[v] = f[v - coins[i]] + 1;
else
f[v] = min(f[v], f[v - coins[i]] + 1);
}
}
}
}
return (f[amount] == -2) ? -1 : f[amount];
    }

};

LeetCode 72Edit 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:

  1. Insert a character
  2. Delete a character
  3. 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(真的很不应该),完了一看别人的解答:This is a classic problem of Dynamic Programming微笑。去你的小jerry......还是自己太菜了。

这里我们使用的状态值是dp[i][j],表示将word1[0...i-1]变化为word2[0......j-1]所需要的最少的操作数。首先我们可以确定边界值:dp[i][0] = i,dp[0][j]=j

接下来推导更新公式:

如果word[i-1] == word[j-1],那么dp[i][j] = dp[i-1][j-1],这是最简单的情况;

如果不相等,则要考虑如下三种情况:

1.通过替换word1[i-1]为word2[j-1],使得word1[0......i-1]变化为word2[0......j],这时的dp[i][j] = dp[i-1][j-1] +1;

2.首先我们通过dp[i-1][j]的代价把word1[0......i-1]中的word1[0......i-2]变为word2[0......j-1],然后再删掉word1[i-1]就可以了,这时dp[i][j] = dp[i-1][j] + 1;

3.首先通过dp[i][j-1]的代价把word1[0....i-1]变换成word2[0......j-2],再加上word2[j-1]即可,这时dp[i][j] = dp[i][j-1] + 1;

那么很明显,更新公式为

dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1) word1[i-1] != word2[j-1]

class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size(); int m = word2.size();
vector<vector <int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++)
dp[i][0] = i;
for (int j = 1; j <= m; j++)
dp[0][j] = j;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (word1[i - 1] == word2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1);
}
}
return dp[n][m];
}
private:
int min(int a, int b, int c)
{
int res;
if (a < b)
res = a;
else
res = b;
if (res < c)
return res;
else
res = c;
return res;
}
};

<think>我们正在分析Android AudioFlinger的日志信息,具体是关于createAudioPatch_l函数返回状态的含义。用户提供的日志片段是:"createAudioPatch_l status 0"。我们需要解释这个状态码0的含义,并把它放在Android音频系统的上下文中。 在Android音频系统中,AudioFlinger负责音频流的混合和路由。createAudioPatch_l函数用于创建音频路由补丁,将音频源(source)和音频汇(sink)连接起来。这个函数的返回值(即status)表示操作的结果。 在Android系统中,许多函数使用状态码来表示操作是否成功。通常,状态码0表示操作成功,没有错误。这遵循了Unix/Linux系统中C/C++程序返回0表示成功的惯例。 在AudioFlinger的上下文中,createAudioPatch_l函数返回0表示音频补丁创建成功。这意味着请求的音频路由(例如,从某个源设备到某个汇设备)已经成功建立。 参考Android源代码,我们可以找到相关的定义。在AudioFlinger.cpp文件中,createAudioPatch_l函数返回的是status_t类型,而status_t在系统中有定义,其中0(OK)表示成功。 因此,日志"createAudioPatch_l status 0"表示音频补丁创建成功。 下面,我们将按照要求组织答案,包括必要的数学表达式(如果有的话)和代码示例(如果有的话),并生成相关问题。 注意:由于用户的问题是关于日志状态码的,可能不需要复杂的数学公式,但为了符合要求,我们可以在适当的地方加入一些公式(比如在解释路由时使用集合表示等)。但如果没有必要,不要强行加入。 另外,用户之前的问题是关于另一条日志的,但这次是新的问题,所以我们独立回答。 结构: 1. 解释状态码0的含义 2. 说明在什么情况下会看到这个日志 3. 可能的状态码错误示例(作为对比) 4. 总结 然后生成相关问题。 注意:由于用户要求使用中文,所以整个回答使用中文。</think>### Android AudioFlinger `createAudioPatch_l status 0` 日志解析 #### 1. **状态码含义** `createAudioPatch_l status 0` 表示**音频路由补丁创建成功**。在 Android 音频系统中: - `status 0` 对应系统常量 `OK`(定义于 `system/core/libutils/Errors.h`) - 数学表达:$ \text{status} = 0 \equiv \text{AUDIO\_STATUS\_OK} $ - 此状态确认 AudioFlinger 已成功建立音频源(source)与音频汇(sink)之间的物理/逻辑通道[^1] #### 2. **典型工作流程** ```mermaid sequenceDiagram 应用->>+AudioPolicyService: 请求音频路由变更 AudioPolicyService->>+AudioFlinger: 调用createAudioPatch() AudioFlinger->>+HAL层: 执行createAudioPatch_l() HAL层-->>-AudioFlinger: 返回status 0 AudioFlinger-->>-应用: 确认路由建立 ``` #### 3. **关键参数解析** 在完整日志中需关注: ```log createAudioPatch_l: num_sources=1, num_sinks=1 sources[0]: type=0(DEVICE), id=0, device=0x4(SPEAKER) sinks[0]: type=0, id=1, device=0x8(HEADSET) status=0 ``` - **设备类型编码**(二进制位映射): $$ \text{Device} = \sum_{i} 2^{bit\_position} $$ - `0x4` (0100b) = 内置扬声器 - `0x8` (1000b) = 有线耳机 - `0x80` (10000000b) = 蓝牙A2DP[^2] #### 4. **常见非零状态码对比** | 状态码 | 常量 | 含义 | 常见原因 | |--------|------|------|----------| | `0` | `OK` | 成功 | 正常路由切换 | | `-22` | `-EINVAL` | 参数错误 | 设备类型冲突 | | `-38` | `-ENOSYS` | 未实现 | HAL驱动不支持 | | `-16` | `-EBUSY` | 资源忙 | 设备被占用 | #### 5. **调试建议** 1. **查看当前路由**: ```bash adb shell dumpsys audio | grep "Active audio patch" -A 5 ``` 2. **强制路由测试**(需 root): ```bash adb shell audioservice set-force-use 2 # 强制扬声器 adb shell audioservice set-force-use 3 # 强制耳机 ``` #### 6. **底层实现原理** 在 `AudioFlinger.cpp` 中: ```cpp status_t AudioFlinger::createAudioPatch_l( const struct audio_patch* patch, audio_patch_handle_t* handle ) { // ...硬件抽象层调用 status_t status = mAudioHwDev->hwDevice()->create_audio_patch(...); if (status == OK) { // status 0 判断点 ALOGV("createAudioPatch_l status %d", status); } return status; } ``` 成功时系统将更新音频路由矩阵: $$ \begin{bmatrix} \text{Source}_1 \\ \vdots \\ \text{Source}_m \end{bmatrix} \xrightarrow{\text{Patch}} \begin{bmatrix} \text{Sink}_1 \\ \vdots \\ \text{Sink}_n \end{bmatrix} $$ --- ### 相关问题 1. 如何解读 `createAudioPatch_l` 日志中的 `device=0x400` 参数? 2. `status -22 (EINVAL)` 错误通常由哪些配置问题引起? 3. Android 13 新增的 `AUDIO_DEVICE_OUT_BLE_SPEAKER` 设备类型值是多少? 4. 多路混音时如何通过日志验证所有音频流正确路由? [^1]: [Android AudioPolicyManager 源码](https://android.googlesource.com/platform/frameworks/av/+/refs/heads/master/services/audiopolicy/managerdefault/AudioPolicyManager.cpp) [^2]: [audio_policy_configuration.xsd 设备类型定义](https://android.googlesource.com/platform/system/media/+/refs/heads/master/audio_utils/include/system/audio.h)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值