动态规划---观察优化枚举(股票系列问题)

打印 上一主题 下一主题

主题 1000|帖子 1000|积分 3000

 
 
  
   121. 买卖股票的最佳机遇 - 力扣(LeetCode)
  1. public class Code01_Stock1 {
  2.         public static int maxProfit(int[] prices) {
  3.                 int ans = 0;
  4.                 for (int i = 1, min = prices[0]; i < prices.length; i++) {
  5.                         // min : 0...i范围上的最小值
  6.                         min = Math.min(min, prices[i]);
  7.                         ans = Math.max(ans, prices[i] - min);
  8.                 }
  9.                 return ans;
  10.         }
  11. }
复制代码
 从0 - i上的最大利润:在i天的时候卖,必要0-i上的最小值;不在i上卖,则必要0 - i-1天上的最大利润,比较即可



122. 买卖股票的最佳机遇 II - 力扣(LeetCode)
  1. public class Code02_Stock2 {
  2.         public static int maxProfit(int[] prices) {
  3.                 int ans = 0;
  4.                 for (int i = 1; i < prices.length; i++) {
  5.                         ans += Math.max(prices[i] - prices[i - 1], 0);
  6.                 }
  7.                 return ans;
  8.         }
  9. }
复制代码
只要两天之间的差值为正数,即有利润,就购买;否则不买



  123. 买卖股票的最佳机遇 III - 力扣(LeetCode)
  1. public class Code03_Stock3 {
  2.         // 完全不优化枚举的方法
  3.         // 通过不了,会超时
  4.         public static int maxProfit1(int[] prices) {
  5.                 int n = prices.length;
  6.                 // dp1[i] : 0...i范围上发生一次交易,不要求在i的时刻卖出,最大利润是多少
  7.                 int[] dp1 = new int[n];
  8.                 for (int i = 1, min = prices[0]; i < n; i++) {
  9.                         min = Math.min(min, prices[i]);
  10.                         dp1[i] = Math.max(dp1[i - 1], prices[i] - min);
  11.                 }
  12.                 // dp2[i] : 0...i范围上发生两次交易,并且第二次交易在i时刻卖出,最大利润是多少
  13.                 int[] dp2 = new int[n];
  14.                 int ans = 0;
  15.                 for (int i = 1; i < n; i++) {
  16.                         // 第二次交易一定要在i时刻卖出
  17.                         for (int j = 0; j <= i; j++) {
  18.                                 // 枚举第二次交易的买入时机j <= i
  19.                                 dp2[i] = Math.max(dp2[i], dp1[j] + prices[i] - prices[j]);
  20.                         }
  21.                         ans = Math.max(ans, dp2[i]);
  22.                 }
  23.                 return ans;
  24.         }
  25.         // 观察出优化枚举的方法
  26.         // 引入best数组,需要分析能力
  27.         public static int maxProfit2(int[] prices) {
  28.                 int n = prices.length;
  29.                 // dp1[i] : 0...i范围上发生一次交易,不要求在i的时刻卖出,最大利润是多少
  30.                 int[] dp1 = new int[n];
  31.                 for (int i = 1, min = prices[0]; i < n; i++) {
  32.                         min = Math.min(min, prices[i]);
  33.                         dp1[i] = Math.max(dp1[i - 1], prices[i] - min);
  34.                 }
  35.                 // best[i] : 0...i范围上,所有的dp1[i]-prices[i],最大值是多少
  36.                 // 这是数组的设置完全是为了替代最后for循环的枚举行为
  37.                 int[] best = new int[n];
  38.                 best[0] = dp1[0] - prices[0];
  39.                 for (int i = 1; i < n; i++) {
  40.                         best[i] = Math.max(best[i - 1], dp1[i] - prices[i]);
  41.                 }
  42.                 // dp2[i] : 0...i范围上发生两次交易,并且第二次交易在i时刻卖出,最大利润是多少
  43.                 int[] dp2 = new int[n];
  44.                 int ans = 0;
  45.                 for (int i = 1; i < n; i++) {
  46.                         // 不需要枚举了
  47.                         // 因为,best[i]已经揭示了,0...i范围上,所有的dp1[i]-prices[i],最大值是多少
  48.                         dp2[i] = best[i] + prices[i];
  49.                         ans = Math.max(ans, dp2[i]);
  50.                 }
  51.                 return ans;
  52.         }
  53.         // 发现所有更新行为都可以放在一起
  54.         // 并不需要写多个并列的for循环
  55.         // 就是等义改写,不需要分析能力
  56.         public static int maxProfit3(int[] prices) {
  57.                 int n = prices.length;
  58.                 int[] dp1 = new int[n];
  59.                 int[] best = new int[n];
  60.                 best[0] = -prices[0];
  61.                 int[] dp2 = new int[n];
  62.                 int ans = 0;
  63.                 for (int i = 1, min = prices[0]; i < n; i++) {
  64.                         min = Math.min(min, prices[i]);
  65.                         dp1[i] = Math.max(dp1[i - 1], prices[i] - min);
  66.                         best[i] = Math.max(best[i - 1], dp1[i] - prices[i]);
  67.                         dp2[i] = best[i] + prices[i];
  68.                         ans = Math.max(ans, dp2[i]);
  69.                 }
  70.                 return ans;
  71.         }
  72.         // 发现只需要有限几个变量滚动更新下去就可以了
  73.         // 空间压缩的版本
  74.         // 就是等义改写,不需要分析能力
  75.         public static int maxProfit4(int[] prices) {
  76.                 int dp1 = 0;
  77.                 int best = -prices[0];
  78.                 int ans = 0;
  79.                 for (int i = 1, min = prices[0]; i < prices.length; i++) {
  80.                         min = Math.min(min, prices[i]);
  81.                         dp1 = Math.max(dp1, prices[i] - min);
  82.                         best = Math.max(best, dp1 - prices[i]);
  83.                         ans = Math.max(ans, best + prices[i]); // ans = Math.max(ans, dp2);
  84.                 }
  85.                 return ans;
  86.         }
  87. }
复制代码




188. 买卖股票的最佳机遇 IV - 力扣(LeetCode)
  1. public class Code04_Stock4 {
  2.         // 就是股票问题2
  3.         public static int free(int[] prices) {
  4.                 int ans = 0;
  5.                 for (int i = 1; i < prices.length; i++) {
  6.                         ans += Math.max(prices[i] - prices[i - 1], 0);
  7.                 }
  8.                 return ans;
  9.         }
  10.         public static int maxProfit1(int k, int[] prices) {
  11.                 int n = prices.length;
  12.                 if (k >= n / 2) {
  13.                         // 这是一个剪枝
  14.                         // 如果k >= n / 2,那么代表所有上坡都可以抓到
  15.                         // 所有上坡都可以抓到 == 交易次数无限,所以回到股票问题2
  16.                         return free(prices);
  17.                 }
  18.                 int[][] dp = new int[k + 1][n];
  19.                 for (int i = 1; i <= k; i++) {
  20.                         for (int j = 1; j < n; j++) {
  21.                                 dp[i][j] = dp[i][j - 1];
  22.                                 for (int p = 0; p < j; p++) {
  23.                                         dp[i][j] = Math.max(dp[i][j], dp[i - 1][p] + prices[j] - prices[p]);
  24.                                 }
  25.                         }
  26.                 }
  27.                 return dp[k][n - 1];
  28.         }
  29.         public static int maxProfit2(int k, int[] prices) {
  30.                 int n = prices.length;
  31.                 if (k >= n / 2) {
  32.                         // 这是一个剪枝
  33.                         // 如果k >= n / 2,那么代表所有上坡都可以抓到
  34.                         // 所有上坡都可以抓到 == 交易次数无限,所以回到股票问题2
  35.                         return free(prices);
  36.                 }
  37.                 int[][] dp = new int[k + 1][n];
  38.                 for (int i = 1, best; i <= k; i++) {
  39.                         best = dp[i - 1][0] - prices[0];
  40.                         for (int j = 1; j < n; j++) {
  41.                                 // 用best变量替代了枚举行为
  42.                                 dp[i][j] = Math.max(dp[i][j - 1], best + prices[j]);
  43.                                 best = Math.max(best, dp[i - 1][j] - prices[j]);
  44.                         }
  45.                 }
  46.                 return dp[k][n - 1];
  47.         }
  48.         // 对方法2进行空间压缩的版本
  49.         public static int maxProfit3(int k, int[] prices) {
  50.                 int n = prices.length;
  51.                 if (k >= n / 2) {
  52.                         // 这是一个剪枝
  53.                         // 如果k >= n / 2,那么代表所有上坡都可以抓到
  54.                         // 所有上坡都可以抓到 == 交易次数无限,所以回到股票问题2
  55.                         return free(prices);
  56.                 }
  57.                 int[] dp = new int[n];
  58.                 for (int i = 1, best, tmp; i <= k; i++) {
  59.                         best = dp[0] - prices[0];
  60.                         for (int j = 1; j < n; j++) {
  61.                                 tmp = dp[j];
  62.                                 dp[j] = Math.max(dp[j - 1], best + prices[j]);
  63.                                 best = Math.max(best, tmp - prices[j]);
  64.                         }
  65.                 }
  66.                 return dp[n - 1];
  67.         }
  68. }
复制代码
 dp[j]表示从0-j天最多买卖i次所能获得的最大收益
 


714. 买卖股票的最佳机遇含手续费 - 力扣(LeetCode)
  1. public class Code05_Stack5 {
  2.         public static int maxProfit(int[] prices, int fee) {
  3.                 // prepare : 交易次数无限制情况下,获得收益的同时扣掉了一次购买和手续费之后,最好的情况
  4.                 int prepare = -prices[0] - fee;
  5.                 // done : 交易次数无限制情况下,能获得的最大收益
  6.                 int done = 0;
  7.                 for (int i = 1; i < prices.length; i++) {
  8.                         done = Math.max(done, prepare + prices[i]);
  9.                         prepare = Math.max(prepare, done - prices[i] - fee);
  10.                 }
  11.                 return done;
  12.         }
  13. }
复制代码
手续费无非是在卖出的时候多出一笔钱,所以和无限次购买股票是一样的



 309. 买卖股票的最佳机遇含冷冻期 - 力扣(LeetCode)

 
  1. public class Code06_Stack6 {
  2.         public static int maxProfit1(int[] prices) {
  3.                 int n = prices.length;
  4.                 if (n < 2) {
  5.                         return 0;
  6.                 }
  7.                 // prepare[i] : 0...i范围上,可以做无限次交易,获得收益的同时一定要扣掉一次购买,所有情况中的最好情况
  8.                 int[] prepare = new int[n];
  9.                 // done[i] : 0...i范围上,可以做无限次交易,能获得的最大收益
  10.                 int[] done = new int[n];
  11.                 prepare[1] = Math.max(-prices[0], -prices[1]);
  12.                 done[1] = Math.max(0, prices[1] - prices[0]);
  13.                 for (int i = 2; i < n; i++) {
  14.                         done[i] = Math.max(done[i - 1], prepare[i - 1] + prices[i]);
  15.                         prepare[i] = Math.max(prepare[i - 1], done[i - 2] - prices[i]);
  16.                 }
  17.                 return done[n - 1];
  18.         }
  19.         // 只是把方法1做了变量滚动更新(空间压缩)
  20.         // 并没有新的东西
  21.         public static int maxProfit2(int[] prices) {
  22.                 int n = prices.length;
  23.                 if (n < 2) {
  24.                         return 0;
  25.                 }
  26.                 // prepare : prepare[i-1]
  27.                 int prepare = Math.max(-prices[0], -prices[1]);
  28.                 // done2 : done[i-2]
  29.                 int done2 = 0;
  30.                 // done1 : done[i-1]
  31.                 int done1 = Math.max(0, prices[1] - prices[0]);
  32.                 for (int i = 2, curDone; i < n; i++) {
  33.                         // curDone : done[i]
  34.                         curDone = Math.max(done1, prepare + prices[i]);
  35.                         // prepare : prepare[i-1] -> prepare[i]
  36.                         prepare = Math.max(prepare, done2 - prices[i]);
  37.                         done2 = done1;
  38.                         done1 = curDone;
  39.                 }
  40.                 return done1;
  41.         }
  42. }
复制代码
 


  903. DI 序列的有效排列 - 力扣(LeetCode)
  1. public class Solution {
  2.         public static int numPermsDISequence1(String s) {
  3.                 return f(s.toCharArray(), 0, s.length() + 1, s.length() + 1);
  4.         }
  5.         // 猜法很妙!
  6.         // 一共有n个数字,位置范围0~n-1
  7.         // 当前来到i位置,i-1位置的数字已经确定,i位置的数字还没确定
  8.         // i-1位置和i位置的关系,是s[i-1] : D、I
  9.         // 0~i-1范围上是已经使用过的数字,i个
  10.         // 还没有使用过的数字中,比i-1位置的数字小的,有less个
  11.         // 还没有使用过的数字中,比i-1位置的数字大的,有n - i - less个
  12.         // 返回后续还有多少种有效的排列
  13.         public static int f(char[] s, int i, int less, int n) {
  14.                 int ans = 0;
  15.                 if (i == n) {
  16.                         ans = 1;
  17.                 } else if (i == 0 || s[i - 1] == 'D') {
  18.                         for (int nextLess = 0; nextLess < less; nextLess++) {
  19.                                 ans += f(s, i + 1, nextLess, n);
  20.                         }
  21.                 } else {
  22.                         for (int nextLess = less, k = 1; k <= n - i - less; k++, nextLess++) {
  23.                                 ans += f(s, i + 1, nextLess, n);
  24.                         }
  25.                 }
  26.                 return ans;
  27.         }
  28.         public static int numPermsDISequence2(String str) {
  29.                 int mod = 1000000007;
  30.                 char[] s = str.toCharArray();
  31.                 int n = s.length + 1;
  32.                 int[][] dp = new int[n + 1][n + 1];
  33.                 for (int less = 0; less <= n; less++) {
  34.                         dp[n][less] = 1;
  35.                 }
  36.                 for (int i = n - 1; i >= 0; i--) {
  37.                         for (int less = 0; less <= n; less++) {
  38.                                 if (i == 0 || s[i - 1] == 'D') {
  39.                                         for (int nextLess = 0; nextLess < less; nextLess++) {
  40.                                                 dp[i][less] = (dp[i][less] + dp[i + 1][nextLess]) % mod;
  41.                                         }
  42.                                 } else {
  43.                                         for (int nextLess = less, k = 1; k <= n - i - less; k++, nextLess++) {
  44.                                                 dp[i][less] = (dp[i][less] + dp[i + 1][nextLess]) % mod;
  45.                                         }
  46.                                 }
  47.                         }
  48.                 }
  49.                 return dp[0][n];
  50.         }
  51.         // 通过观察方法2,得到优化枚举的方法
  52.         public static int numPermsDISequence(String str) {
  53.                 int mod = 1000000007;
  54.                 char[] s = str.toCharArray();
  55.                 int n = s.length + 1;
  56.                 int[][] dp = new int[n + 1][n + 1];
  57.                 for (int less = 0; less <= n; less++) {
  58.                         dp[n][less] = 1;
  59.                 }
  60.                 for (int i = n - 1; i >= 0; i--) {
  61.                         if (i == 0 || s[i - 1] == 'D') {
  62.                                 dp[i][1] = dp[i + 1][0];
  63.                                 for (int less = 2; less <= n; less++) {
  64.                                         dp[i][less] = (dp[i][less - 1] + dp[i + 1][less - 1]) % mod;
  65.                                 }
  66.                         } else {
  67.                                 dp[i][n - i - 1] = dp[i + 1][n - i - 1];
  68.                                 for (int less = n - i - 2; less >= 0; less--) {
  69.                                         dp[i][less] = (dp[i][less + 1] + dp[i + 1][less]) % mod;
  70.                                 }
  71.                         }
  72.                 }
  73.                 return dp[0][n];
  74.         }
  75. }
复制代码




1235. 规划兼职工作 - 力扣(LeetCode)


 
  1. public class Code01_MaximumProfitInJobScheduling {
  2.         public static int MAXN = 50001;
  3.         public static int[][] jobs = new int[MAXN][3];
  4.         public static int[] dp = new int[MAXN];
  5.         public static int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
  6.                 int n = startTime.length;
  7.                 for (int i = 0; i < n; i++) {
  8.                         jobs[i][0] = startTime[i];
  9.                         jobs[i][1] = endTime[i];
  10.                         jobs[i][2] = profit[i];
  11.                 }
  12.                 // 工作按照结束时间从小到大排序
  13.                 Arrays.sort(jobs, 0, n, (a, b) -> a[1] - b[1]);
  14.                 dp[0] = jobs[0][2];
  15.                 for (int i = 1, start; i < n; i++) {
  16.                         start = jobs[i][0];
  17.                         dp[i] = jobs[i][2];
  18.                         if (jobs[0][1] <= start) {
  19.                                 dp[i] += dp[find(i - 1, start)];
  20.                         }
  21.                         dp[i] = Math.max(dp[i], dp[i - 1]);
  22.                 }
  23.                 return dp[n - 1];
  24.         }
  25.         // job[0...i]范围上,找到结束时间 <= start,最右的下标
  26.         public static int find(int i, int start) {
  27.                 int ans = 0;
  28.                 int l = 0;
  29.                 int r = i;
  30.                 int m;
  31.                 while (l <= r) {
  32.                         m = (l + r) / 2;
  33.                         if (jobs[m][1] <= start) {
  34.                                 ans = m;
  35.                                 l = m + 1;
  36.                         } else {
  37.                                 r = m - 1;
  38.                         }
  39.                 }
  40.                 return ans;
  41.         }
  42. }
复制代码
按照结束时间从小到大排序,才不会错过最大的利益



629. K 个逆序对数组 - 力扣(LeetCode)
  1. public class Solution {
  2.         // 最普通的动态规划
  3.         // 不优化枚举
  4.         public static int kInversePairs1(int n, int k) {
  5.                 int mod = 1000000007;
  6.                 // dp[i][j] : 1、2、3...i这些数字,形成的排列一定要有j个逆序对,请问这样的排列有几种
  7.                 int[][] dp = new int[n + 1][k + 1];
  8.                 dp[0][0] = 1;
  9.                 for (int i = 1; i <= n; i++) {
  10.                         dp[i][0] = 1;
  11.                         for (int j = 1; j <= k; j++) {
  12.                                 if (i > j) {
  13.                                         for (int p = 0; p <= j; p++) {
  14.                                                 dp[i][j] = (dp[i][j] + dp[i - 1][p]) % mod;
  15.                                         }
  16.                                 } else {
  17.                                         // i <= j
  18.                                         for (int p = j - i + 1; p <= j; p++) {
  19.                                                 dp[i][j] = (dp[i][j] + dp[i - 1][p]) % mod;
  20.                                         }
  21.                                 }
  22.                         }
  23.                 }
  24.                 return dp[n][k];
  25.         }
  26.     public static int kInversePairs(int n, int k) {
  27.                 int mod = 1000000007;
  28.                 int[][] dp = new int[n + 1][k + 1];
  29.                 dp[0][0] = 1;
  30.                 // window : 窗口的累加和
  31.                 for (int i = 1, window; i <= n; i++) {
  32.                         dp[i][0] = 1;
  33.                         window = 1;
  34.                         for (int j = 1; j <= k; j++) {
  35.                                 if (i > j) {
  36.                                         window = (window + dp[i - 1][j]) % mod;
  37.                                 } else {
  38.                                         // i <= j
  39.                                         window = ((window + dp[i - 1][j]) % mod - dp[i - 1][j - i] + mod) % mod;
  40.                                 }
  41.                                 dp[i][j] = window;
  42.                         }
  43.                 }
  44.                 return dp[n][k];
  45.         }
  46. }
复制代码


 

514. 自由之路 - 力扣(LeetCode)
  1. public class Solution {
  2.         // 为了让所有语言的同学都可以理解
  3.         // 不会使用任何java语言自带的数据结构
  4.         // 只使用最简单的数组结构
  5.         public static int MAXN = 101;
  6.         public static int MAXC = 26;
  7.         public static int[] ring = new int[MAXN];
  8.         public static int[] key = new int[MAXN];
  9.         public static int[] size = new int[MAXC];
  10.         public static int[][] where = new int[MAXC][MAXN];
  11.         public static int[][] dp = new int[MAXN][MAXN];
  12.         public static int n, m;
  13.         public static void build(String r, String k) {
  14.                 for (int i = 0; i < MAXC; i++) {
  15.                         size[i] = 0;
  16.                 }
  17.                 n = r.length();
  18.                 m = k.length();
  19.                 for (int i = 0, v; i < n; i++) {
  20.                         v = r.charAt(i) - 'a';
  21.                         where[v][size[v]++] = i;
  22.                         ring[i] = v;
  23.                 }
  24.                 for (int i = 0; i < m; i++) {
  25.                         key[i] = k.charAt(i) - 'a';
  26.                 }
  27.                 for (int i = 0; i < n; i++) {
  28.                         for (int j = 0; j < m; j++) {
  29.                                 dp[i][j] = -1;
  30.                         }
  31.                 }
  32.         }
  33.         public static int findRotateSteps(String r, String k) {
  34.                 build(r, k);
  35.                 return f(0, 0);
  36.         }
  37.         // 指针当前指着轮盘i位置的字符,要搞定key[j....]所有字符,最小代价返回
  38.         public static int f(int i, int j) {
  39.                 if (j == m) {
  40.                         // key长度是m
  41.                         // 都搞定
  42.                         return 0;
  43.                 }
  44.                 if (dp[i][j] != -1) {
  45.                         return dp[i][j];
  46.                 }
  47.                 int ans;
  48.                 if (ring[i] == key[j]) {
  49.                         // ring b
  50.                         //      i
  51.                         // key  b
  52.                         //      j
  53.                         ans = 1 + f(i, j + 1);
  54.                 } else {
  55.                         // 轮盘处在i位置,ring[i] != key[j]
  56.                         // jump1 : 顺时针找到最近的key[j]字符在轮盘的什么位置
  57.                         // distance1 : 从i顺时针走向jump1有多远
  58.                         int jump1 = clock(i, key[j]);
  59.                         int distance1 = (jump1 > i ? (jump1 - i) : (n - i + jump1));
  60.                         // jump2 : 逆时针找到最近的key[j]字符在轮盘的什么位置
  61.                         // distance2 : 从i逆时针走向jump2有多远
  62.                         int jump2 = counterClock(i, key[j]);
  63.                         int distance2 = (i > jump2 ? (i - jump2) : (i + n - jump2));
  64.                         ans = Math.min(distance1 + f(jump1, j), distance2 + f(jump2, j));
  65.                 }
  66.                 dp[i][j] = ans;
  67.                 return ans;
  68.         }
  69.         // 从i开始,顺时针找到最近的v在轮盘的什么位置
  70.         public static int clock(int i, int v) {
  71.                 int l = 0;
  72.                 // size[v] : 属于v这个字符的下标有几个
  73.                 int r = size[v] - 1, m;
  74.                 // sorted[0...size[v]-1]收集了所有的下标,并且有序
  75.                 int[] sorted = where[v];
  76.                 int find = -1;
  77.                 // 有序数组中,找>i尽量靠左的下标
  78.                 while (l <= r) {
  79.                         m = (l + r) / 2;
  80.                         if (sorted[m] > i) {
  81.                                 find = m;
  82.                                 r = m - 1;
  83.                         } else {
  84.                                 l = m + 1;
  85.                         }
  86.                 }
  87.                 // 找到了就返回
  88.                 // 没找到,那i顺指针一定先走到最小的下标
  89.                 return find != -1 ? sorted[find] : sorted[0];
  90.         }
  91.         public static int counterClock(int i, int v) {
  92.                 int l = 0;
  93.                 int r = size[v] - 1, m;
  94.                 int[] sorted = where[v];
  95.                 int find = -1;
  96.                 // 有序数组中,找<i尽量靠右的下标
  97.                 while (l <= r) {
  98.                         m = (l + r) / 2;
  99.                         if (sorted[m] < i) {
  100.                                 find = m;
  101.                                 l = m + 1;
  102.                         } else {
  103.                                 r = m - 1;
  104.                         }
  105.                 }
  106.                 // 找到了就返回
  107.                 // 没找到,那i逆指针一定先走到最大的下标
  108.                 return find != -1 ? sorted[find] : sorted[size[v] - 1];
  109.         }
  110. }
复制代码



 未排序数组中累加和小于或等于给定值的最宗子数组长度_牛客题霸_牛客网 (nowcoder.com)
  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.io.StreamTokenizer;
  7. // 至今的最优解,全网题解几乎都是我几年前讲的方法
  8. public class Main {
  9.     public static int MAXN = 100001;
  10.     public static int[] nums = new int[MAXN];
  11.     public static int[] minSums = new int[MAXN];
  12.     public static int[] minSumEnds = new int[MAXN];
  13.     public static int n, k;
  14.     public static void main(String[] args) throws IOException {
  15.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  16.         StreamTokenizer in = new StreamTokenizer(br);
  17.         PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
  18.         while (in.nextToken() != StreamTokenizer.TT_EOF) {
  19.             n = (int) in.nval;
  20.             in.nextToken();
  21.             k = (int) in.nval;
  22.             for (int i = 0; i < n; i++) {
  23.                 in.nextToken();
  24.                 nums[i] = (int) in.nval;
  25.             }
  26.             out.println(compute1());
  27.         }
  28.         out.flush();
  29.         out.close();
  30.         br.close();
  31.     }
  32.     public static int compute1() {
  33.         int[] sums = new int[n + 1];
  34.         for (int i = 0, sum = 0; i < n; i++) {
  35.             // sum : 0...i范围上,这前i+1个数字的累加和
  36.             sum += nums[i];
  37.             // sums[i + 1] : 前i+1个,包括一个数字也没有的时候,所有前缀和中的最大值
  38.             sums[i + 1] = Math.max(sum, sums[i]);
  39.         }
  40.         int ans = 0;
  41.         for (int i = 0, sum = 0, pre, len; i < n; i++) {
  42.             sum += nums[i];
  43.             pre = find(sums, sum - k);
  44.             len = pre == -1 ? 0 : i - pre + 1;
  45.             ans = Math.max(ans, len);
  46.         }
  47.         return ans;
  48.     }
  49.     public static int find(int[] sums, int num) {
  50.         int l = 0;
  51.         int r = n;
  52.         int m = 0;
  53.         int ans = -1;
  54.         while (l <= r) {
  55.             m = (l + r) / 2;
  56.             if (sums[m] >= num) {
  57.                 ans = m;
  58.                 r = m - 1;
  59.             } else {
  60.                 l = m + 1;
  61.             }
  62.         }
  63.         return ans;
  64.     }
  65.     public static int compute2() {
  66.         minSums[n - 1] = nums[n - 1];
  67.         minSumEnds[n - 1] = n - 1;
  68.         for (int i = n - 2; i >= 0; i--) {
  69.             if (minSums[i + 1] < 0) {
  70.                 minSums[i] = nums[i] + minSums[i + 1];
  71.                 minSumEnds[i] = minSumEnds[i + 1];
  72.             } else {
  73.                 minSums[i] = nums[i];
  74.                 minSumEnds[i] = i;
  75.             }
  76.         }
  77.         int ans = 0;
  78.         for (int i = 0, sum = 0, end = 0; i < n; i++) {
  79.             while (end < n && sum + minSums[end] <= k) {
  80.                 sum += minSums[end];
  81.                 end = minSumEnds[end] + 1;
  82.             }
  83.             if (end > i) {
  84.                 // 如果end > i,
  85.                 // 窗口范围:i...end-1,那么窗口有效
  86.                 ans = Math.max(ans, end - i);
  87.                 sum -= nums[i];
  88.             } else {
  89.                 // 如果end == i,那么说明窗口根本没扩出来,代表窗口无效
  90.                 // end来到i+1位置,然后i++了
  91.                 // 继续以新的i位置做开头去扩窗口
  92.                 end = i + 1;
  93.             }
  94.         }
  95.         return ans;
  96.     }
  97. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表