嵌入式软件岗,两道编程题都是动态规划。
第一题:求出数组中两个不重叠的子数组的最大和。
POJ 2479Maximum sum http://poj.org/problem?id=2479
#include
#include
using namespace std;
int helper(const vector& v)
{
int n = v.size();
vector left(n), right(n);
//left[i]: 以i结尾的子数组最大和, right[j]: 以j开头的子数组最大和
left[0] = v[0];
for(int i = 1; i < n; i++) left[i] = max(left[i-1], 0) + v[i];
right[n-1] = v[n-1];
for(int j = n - 2; j >= 0; j--) right[j] = max(right[j+1], 0) + v[j];
vector lmax(n), rmax(n);
//lmax[i]: [0...i]中子数组的最大和, rmax[j]: [j...n-1]中子数组最大和
lmax[0] = left[0];
for(int i = 1; i < n; i++) lmax[i] = max(lmax[i-1], left[i]);
rmax[n-1] = right[n-1];
for(int j = n - 2; j >= 0; j--) rmax[j] = max(rmax[j+1], right[j]);
int ans = -1e9;
for(int i = 0; i < n - 1; i++)
{
ans = max(ans, lmax[i] + rmax[i+1]);
}
return ans;
}
int main()
{
int q;
scanf("%d", &q);
for(int query = q; query > 0; query--)
{
int n;
scanf("%d", &n);
vector v(n);
for(int i = 0; i < n; i++) scanf("%d", &v[i]);
printf("%d\n", helper(v));
}
return 0;
}
一道可以采用相似思路去做的题目:
1156. 单字符重复子串的最大长度
如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。
给你一个字符串 text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。
示例 1:
输入:text = "ababa"
输出:3
示例 2:输入:text = "aaabaaa"
输出:6
示例 3:输入:text = "aaabbaaa"
输出:4
示例 4:输入:text = "aaaaa"
输出:5
示例 5:输入:text = "abcdef"
输出:1
提示:
1 <= text.length <= 20000
text 仅由小写英文字母组成。
class Solution {
public:
int maxRepOpt1(string text) {
int n = text.size();
int h[128] = {0};
for(char c : text) h[c]++;
vector s(n, 1), e(n, 1);
//s[i]: 以 text[i] 开头的,只含有 text[i]的最长子串长度
//e[i]: 以 text[i] 结尾的,只含有 text[i]的最长字串长度
int ans = 1;
for(int i = n - 1; i >= 0; i--)
{
int j = i + 1;
while(j < n && text[j] == text[i]) j++;
s[i] = j - i;
//如果s[i] < h[text[i]],则一定可以移动一个相同字符接在后面
ans = max(ans, s[i] < h[text[i]] ? s[i] + 1 : s[i]);
}
for(int i = 0; i < n; i++)
{
int j = i - 1;
while(j >= 0 && text[j] == text[i]) j--;
e[i] = i - j;
}
//如果 i 和 i+2 是相同的字符,中间隔着一个不同字符,那么一定可以把中间的这个字符换掉
for(int i = 0; i < n - 2; i++)
{
if(text[i] != text[i+2] || text[i] == text[i+1]) continue;
int sum = e[i] + s[i+2];
if(sum == h[text[i]]) ans = max(ans, sum);
else ans = max(ans, sum + 1);
}
return ans;
}
};
第二题:类似1235. 规划兼职工作, 但是这一题还要输出使得利润最大的安排方式,考试的时候来不及想怎么得到安排方式,现在想应该可以从dp数组里获得安排的信息,但是没法验证这个想法对不对。根据leetcode 1235的用例测试了一下没有问题。
/*测试用例:第一行样例数t
每个样例,第一行任务数n
随后n行,每行四个数 id, start, end, profit
3
4
1 1 3 50
2 2 4 10
3 3 5 40
4 3 6 70
5
1 1 3 20
2 2 5 20
3 3 10 100
4 4 6 70
5 6 9 60
3
1 1 2 5
2 1 3 6
3 1 4 4
应该输出:
120,1,4
150,1,4,5
6,2
*/
#include
using namespace std;
struct Node
{
int id, s, e, p;
Node(int i_ = 0, int s_ = 0, int e_ = 0, int p_ = 0): id(i_), s(s_), e(e_), p(p_){};
bool operator < (const Node& other) const
{
return e < other.e;
}
};
int main()
{
int query;
scanf("%d", &query);
for(int q = query; q > 0; q--)
{
int n;
scanf("%d", &n);
vector all(n);
for(int i = 0; i < n; i++)
{
scanf("%d%d%d%d", &all[i].id, &all[i].s, &all[i].e, &all[i].p);
}
sort(all.begin(), all.end());
vector dp(n, 0);
dp[0] = all[0].p;
for(int i = 1; i < n; i++)
{
dp[i] = dp[i-1];
auto it = upper_bound(all.begin(), all.begin() + i, Node(0, 0, all[i].s, 0));
if(it != all.begin())
{
--it;
dp[i] = max(dp[i], dp[it-all.begin()] + all[i].p);
}
else dp[i] = max(dp[i], all[i].p);
}
int i = n-1;
vector ans;
//根据dp数组恢复信息
while(i >= 0)
{
if(i > 0 && dp[i] == dp[i-1]) i--;
else
{
ans.push_back(i);
auto it = upper_bound(all.begin(), all.begin() + i, Node(0, 0, all[i].s, 0));
if(it != all.begin())
{
--it;
i = it - all.begin();
continue;
}
else break;
}
}
printf("%d", dp[n-1]);
for(i = ans.size() - 1; i >= 0; i--) printf(",%d",all[ans[i]].id);
printf("\n");
}
return 0;
}