算法思想一:暴力枚举
解题思路:
题目的主要信息:
给一串数组,每次找到一个位置进行一次操作:将该位置前后一个数及本身改为三者中的最大值
一共进行n次操作,每次操作需要找到改变后使数组和最大那个位置来改变
利用二进制掩码的特性,枚举所有位置选择或是不选择的组合(相当于n次操作,选择的位置进行排列足组合),按照顺序模拟改变的值,最后更新较大值即可。
注:由于此方法需要进行二进制枚举所有可能性,则最终时间复杂度较高会超时,此方法做了解即可
代码展示:
C++版本
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param n int整型 数字个数 * @param a int整型vector 待操作序列 * @return int整型vector */ vector solve(int n, vector<int> a){ vector res(n, 0); for(int i = 1; i < (1 << n); i++){ //枚举数字的可能性 int num = 0; vector temp = a; //每次记录修改的数组 for(int j = 0; j < n; j++){ if((i >> j) & 1){ num++; int maxnum = a[j]; //处理边界 if(j - 1 >= 0) maxnum = max(maxnum, temp[j - 1]); if(j + 1 < n) maxnum = max(maxnum, temp[j + 1]); if(j - 1 >= 0) temp[j - 1] = maxnum; if(j + 1 < n) temp[j + 1] = maxnum; temp[j] = maxnum; } } int sum = 0; for(int j = 0; j < n; j++) //求和 sum += temp[j]; res[num - 1] = max(res[num - 1], sum);//更新 } return res; } };
复杂度分析
时间复杂度:二进制枚举时间,再加一层循环
空间复杂的:辅助数组temp每次回归原来的数组
算法思想二:动态规划
解题思路:
1、要求的最大值等于数组a的元素和加上每次操作后增加最多的大小。因此可以一开始就求出数组a的元素和,然后用辅助数组dp,代表每次操作位置 i 后,i 处的值为 j 时对应的最大总增量。
状态转移方程:
2、再借助队列做一个类似bfs的遍历,队列中记录的分别是元素下标、第几次操作、以当前元素为中的连续三个最大值。对于每次操作,从队列中取出元素,向后扩展,更新最大值。
设置两个dp,其中dp如是记录每次操作后的值,。而dp1是用来在bfs时找到当前操作的最大增加量,最后需要更新进dp中,dp1的更新方式类似的dp
代码展示:
import java.util.*; public class Solution { /** * * @param n int整型 数字个数 * @param a int整型一维数组 待操作序列 * @return int整型一维数组 */ public int[] solve (int n, int[] a) { // write code here int[] ans = new int[n]; // dp记录没次操作后的值 int[][] dp = new int[n][201]; // dp1用来在bfs找到当前操作的最大增加量 int[][] dp1 = new int[n][201]; // 初始化 dp dp1全都为 -1 for (int i = 0; i < n; i++) { Arrays.fill(dp[i], -1); Arrays.fill(dp1[i], -1); } Queue<Sta> q = new LinkedList<>(); int sum = 0; // 处理连续三个数 for (int i = 0; i < n; ++i) { sum += a[i]; int t = a[i], g = a[i], cnt = 1; if (i > 0) { // 非下界 t = Math.max(a[i - 1], t); g += a[i - 1]; // 三个数的总值 cnt++; // 三个元素个数 } if (i < n - 1) { // 非上界 t = Math.max(a[i + 1], t); g += a[i + 1]; cnt++; } q.add(new Sta(i, 1, t)); // 放入队列进行bfs dp[i][t] = Math.max(dp[i][t], cnt * t - g); ans[0] = Math.max(cnt * t - g, ans[0]); } for (int k = 1; k < n; ++k) { int sz = q.size(); for (int i = 0; i < sz; ++i) { // 遍历找到最大增加量 Sta sta = q.poll(); assert sta != null; int pos = sta.pos, kk = sta.k, mx = sta.mx; for (int j = pos + 1; j < n; ++j) { // 向后扩展 mx int w, g, cnt = 2;//扩展状态 if (j == pos + 1) { w = mx; g = 2 * mx; } else if (j == pos + 2) { w = Math.max(mx, a[j]); g = mx + a[j]; } else { w = Math.max(a[j - 1], a[j]); g = a[j - 1] + a[j]; } if (j < n - 1) { w = Math.max(a[j + 1], w); g += a[j + 1]; cnt++; } if (dp1[j][w] == -1) q.add(new Sta(j, kk + 1, w));//保存状态 dp1[j][w] = Math.max(dp1[j][w], cnt * w - g + dp[pos][mx]);//更新结果 ans[kk] = Math.max(dp1[j][w], ans[kk]); } dp[pos][mx] = -1;//还原数组为-1,方便下一轮扩展用 } int[][] temp = dp; dp = dp1; dp1 = temp; } for (int i = 0; i < n; i++) { ans[i] += sum; } return ans; } public class Sta { //pos是下标,k是第几次操作,mx当前连续三个的最大值 int pos; int k; int mx; public Sta(int pos, int k, int mx) { this.pos = pos; this.k = k; this.mx = mx; } } }
class Solution { public: /** * * @param n int整型 数字个数 * @param a int整型vector 待操作序列 * @return int整型vector */ vector<int> ans; struct Sta{ int pos, k, mx; }; vector<int> solve(int n, vector<int>& a) { // write code here ans.resize(n, INT_MIN); int maxe = *max_element(a.begin(), a.end()); vector<vector<int>> dp(201, vector<int>(201, -1)), dp1 = dp; queue<Sta> q; int sum = 0; for(int i = 0; i < n; ++i){ sum += a[i]; int t = a[i], g = a[i], cnt = 1; if(i > 0) t= max(a[i-1], t), g += a[i-1],cnt++; if(i < n - 1) t =max(a[i+1], t), g += a[i+1], cnt++; q.push({i, 1, t}); dp[i][t]=max(dp[i][t], cnt*t-g); ans[0] = max(cnt*t-g, ans[0]); } for(int k = 1; k < n; ++k){ int sz = q.size(); for(int i = 0; i < sz; ++i){ auto sta = q.front(); q.pop(); int pos = sta.pos, kk = sta.k, mx = sta.mx; for(int j = pos+1; j < n; ++j){ int w, g, cnt = 2;//扩展状态 if(j == pos+1) w = mx, g = 2*mx; else if(j == pos+2) w = max(mx, a[j]), g = mx+a[j]; else w = max(a[j-1],a[j]), g = a[j-1]+a[j]; if(j < n - 1) w = max(a[j+1], w), g += a[j+1], cnt++; if(dp1[j][w]==-1) q.push(Sta{j, kk+1, w});//保存状态 dp1[j][w]=max(dp1[j][w], cnt*w-g+dp[pos][mx]);//更新结果 ans[kk] = max(dp1[j][w], ans[kk]); } dp[pos][mx] = -1;//还原数组为-1,方便下一轮扩展用 } swap(dp, dp1); } for(auto &x : ans) x+=sum; return ans; } };
复杂度分析
时间复杂度:,遍历dp矩阵时间
空间复杂度:,建立一个这样的矩阵
算法思想三:四维dp数组动态规划
解题思路:
方法三和方法二的主要思想都是一样的,只是在状态方程转移采用两种表现形式
用表示考虑完前 i 个数字,进行了 j 次操作,在不考虑第 i+1 个数字的情况下第 i 个数字变成了 k 的情况下整个序列的值最多增加多少。最后一维为0表示位置 i 操作了,最后一维为1表示位置 i 没有操作。
那么考虑前 i 个位置进行 j 次操作,第 i 个位置为 k 转移到->对前 i+1 个位置进行 次操作。
如果位置 i+1 不进行操作,有两种转移:
1、位置 i 没有操作过,那么不会对位置 i+1 造成任何影响,
2、位置 i 进行过操作,那么 i-1 位置的数字和位置 i 的数字一定是相等的(特殊考虑 i=0)。这个时候我们判断一下 a(i+1)和 k 的大小,如果是 k 较大,那么 i+1 位置会变成 k 并增加 的贡献,否则位置 i 和位置 i-1 的值都会变成 a[i+1](因为对位置 i 进行过操作),增加 的贡献(对0特判)
如果位置 i+1 进行操作,那么也有两种转移:
1、位置 i 没有操作过,那么对 i+1 的操作只能影响位置 i ,判断一下 a[i+1]和 k 之间的关系并转移即可。
2、位置 i 进行过操作,那么对 i+1 的操作可能会影响 i 和 i-1 两个位置。和上面 i+1 不操作的分支2进行相同的讨论即可。
代码展示:
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param n int整型 数字个数 * @param a int整型vector 待操作序列 * @return int整型vector */ int dp[205][205][205][2]; vector<int> solve(int n, vector<int> a){ int maxn = 205; int inf = 0x3f3f3f3f; int ans = 0; for(int i = 0; i < n; ++i) ans += a[i]; for(int i = 0; i < maxn; ++i) for(int j = 0; j < maxn; ++j) for(int k = 0; k < maxn; ++k) dp[i][j][k][0] = dp[i][j][k][1] = -inf; dp[0][0][a[0]][0] = 0; dp[0][1][a[0]][1] = 0; for(int i = 0; i < n-1; ++i){ for(int j = 0; j <= i+1; ++j){ for(int x = a[i]; x < maxn; ++ x){ //00 dp[i+1][j][a[i+1]][0] = max(dp[i+1][j][a[i+1]][0], dp[i][j][x][0]); //01 if(x > a[i+1]){ dp[i+1][j+1][x][1] = max(dp[i+1][j+1][x][1], dp[i][j][x][0] + x-a[i+1]); }else{ dp[i+1][j+1][a[i+1]][1] = max(dp[i+1][j+1][a[i+1]][1], dp[i][j][x][0]+a[i+1]-x); } //10 if(x > a[i+1]){ dp[i+1][j][x][0] = max(dp[i+1][j][x][0], dp[i][j][x][1]+x-a[i+1]); }else{ dp[i+1][j][a[i+1]][0] = max(dp[i+1][j][a[i+1]][0], dp[i][j][x][1]+(a[i+1]-x)+(i+1>1)*(a[i+1]-x)); } //11 if(x > a[i+1]){ dp[i+1][j+1][x][1] = max(dp[i+1][j+1][x][1], dp[i][j][x][1]+x-a[i+1]); }else{ dp[i+1][j+1][a[i+1]][1] = max(dp[i+1][j+1][a[i+1]][1], dp[i][j][x][1]+(a[i+1]-x)+(i+1>1)*(a[i+1]-x)); } } } } int mx = 0; for(int k = 1; k <= n; ++k){ for(int i = 1; i < maxn; ++i) for(int j = 0; j < 2; ++j) mx = max(mx, dp[n-1][k][i][j]); a[k-1] = ans+mx; } return a; } };
复杂度分析
时间复杂度:,因为要遍历 dp 数组进行递推,数组前2维与n相关,第3维等于,转移的过程是的
空间复杂度:,建立一个这样的矩阵