查看: 2687| 回复: 17
跳转到指定楼层
上一主题 下一主题
收起左侧

[动态规划] 发一道碰到的数组题

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
题目大概是这样的:
给你一个由'.'和'S'组成的长度为N的数组A,你可以最多做K次操作,每次操作选择一个数组中的位置,把相应位置连同左右邻居都变成'.'。
问K次操作之后,数组中最多可以有多少个'.'。
输入:
N K
A
输出
数组A中'.'的个数。

Constraint
1 <= N <= 1000
1 <= K <= 1000

然后我贴几个测试数据
格式:
N K
A
Answer

67 4
SS.S...S....SS..S..S.S.S...SS...SSS..SS.SS.SSSSS...S.S.S...S......S
48

29 2
.S.....SSSSS.SSSS...SS.SSS.SS
18

79 6
.S...SSS.SSS..SSSS.SSSSS.SS.S.SS.SS.SSSSSSS.SS...SS.S.SSS.SS.S.SS..S..S.SSS.SS.
47

743 69
S..S..S.S.S.SS..S...S..S.S.SS...SSS.S...S.S..S.S.SS.SSSSS.SS..SS.SS....SSS.S.S.S..SSS.....S.S.S.S.S..S.SSS..S.SSSSS...SSSSSS..S...S...S.SS.SS.....SS.......S.S.SSS.SSSSSS.S..S...SS.S...SS.SSSSSS.S.S..S..S..S.SS...SS..S..SS.SS.S..SS.S.SS..SSS.S.S.SSS..SS..SSS..S....S..S.S.SSSSSSSSSSSS..S.S.SSS....
563

458 83
..SSSSSS.SSS...SS.S.S..S...S.S..S.....SS.S.S..SSSSSS.SSSS..S.....S.....S.S...S...S....S...S....S.S.SSS....SSS....SSSS.S.........SSSS.S...SS..SS....S.SS..SS.SS.S..SS..SS..SS..SS..S.SS.SSSSSS.S.S....S..S...SS....SS........SS.S....S.S..S.SSSS.S..SSSSSSS.S..S....S.S.SS.SS.SSS.S.....S...S....S..SS...
431

401 72
SSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.S.SSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSS.SSSSSSSSSS.SSSS.SSSSSSS...
238

276 23
SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.
100

480 75
SSSS.SSSSSSSSSS.S.SS.SSSSSSSSSSS.SSS.SSSSSSS.SSSS.SSSSSS.SSS.SSSSSSSSSSSS.SSSS.S.SSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSS.SSSSS.SSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSS..SSSSSS.SS.SSSSSSSSSSSS.SSSSSSS.SSSSSSSSSSSS.SSSSSSSSS...
276

试了几个用模板扫的想法总是被一些case卡到,想想了也不知道怎么dp。dfs可以做但是O(2^N)的。


补充内容 (2019-6-17 14:05):
别的大数据有问题,请使用前3个和倒数第2个。抱歉。

补充内容 (2019-6-18 09:29):
再补充一个数据
6 2
S.SSSS
6

评分

参与人数 1大米 +15 收起 理由
14417335 + 15

查看全部评分


上一篇:在leetcode面经上发现一道有意思的题目,大家可以一起讨论一下
下一篇:求Leetcode 最新题库公司分类
seeker丶 2019-6-15 21:57:19 | 只看该作者
全局:
分享我的一个复杂度是O(NK)的贪心法。用链表管理数组A,每次贪心地从左到右看是否有连续三个S。如果有则都变成.,然后在链表里删除这三个S;如果没有则看是否连续三个里有两个S,有则都变成.,然后在链表里删除这三个字符;以此类推。这样重复K次。

这是我的证明(不一定对?)
先证明一个引理,第一次选中的三个字符一定出现在K次答案的最优解中。反证,如若不然,则任意一个最优解中都不包含这三个字符:
1. 最优解和这三个字符完全没有重叠:把最优解随便一次变换改成变换这三个字符,则解更优,否则与这三个字符是第一次最优的矛盾。
2. 最优解和这三个字符有一个位置重叠:把最优解包含了重叠字符的变换改成变换这三个字符,则解不比原先差,否则与这三个字符是第一次最优的矛盾。
3. 最优解和这三个字符有两个位置重叠:
3.1 重叠位置在最左或者最右:同理2得把包含重叠部分改成这三个字符不会更差。
3.2 重叠位置是左边一个右边一个:比如a b c d e f g,第一次选择了cde,但最优解包含abc和efg。
现在讨论abc中S的数量,显然abc中S的数量不能多于cde,否则与cde是第一次最优的矛盾。
3.2.1 abc中S的数量等于def中S的数量: 则我们的贪心法由于是从左到右的(关键),会选择abc而不是cde,矛盾;(这里为了排除a-g是S.S.S.S的情况)
3.2.2 abc中S的数量少于def中S的数量:所以abc中S的数量少于等于bcd中S的数量,将abc改成bcd,不会使解更差。

这样就证明了,任意一个最优解,都是包含第一次我们选择的三个字符的,这样我们在链表里删掉这三个字符,就把问题的规模变成了N-3,K-1的一样问题,因此复杂度是O(NK)
我用这个算法试了67 29 79 276这4个case都是没问题的(别的好像数据有点问题)
如果有帮助还请加点大米~ 现在有好多东西都看不到呀

补充内容 (2019-6-15 22:02):
3.2.1 和3.2.2中 应该都是abc中S的数量等于(少于)cde(不是def)中S的数量,笔误~

评分

参与人数 2大米 +12 收起 理由
14417335 + 10
Neroldy + 2 很有用的信息!

查看全部评分

回复

使用道具 举报

zjck1995 2019-6-16 23:15:25 | 只看该作者
全局:
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         InputStream inputStream = System.in;
  4.         OutputStream outputStream = System.out;
  5.         Scanner in = new Scanner(inputStream);
  6.         PrintWriter out = new PrintWriter(outputStream);
  7.         Main solver = new Main();
  8.         solver.solve(1, in, out);
  9.         out.close();
  10.     }

  11.     static class Main {
  12.         public void solve(int testNumber, Scanner in, PrintWriter out) {
  13.             int n = in.nextInt();
  14.             int k = in.nextInt();
  15.             String s = in.next();
  16.             int[] a = new int[n + 1];
  17.             for (int i = 1; i <= n; i++) {
  18.                 a[i] = s.charAt(i - 1) == '.' ? 1 : 0;
  19.             }
  20.             int[][] dp = new int[n + 1][k + 1];
  21.             for (int i = 1; i <= n; i++) {
  22.                 dp[i][0] = dp[i - 1][0] + a[i];
  23.                 for (int j = 1; j <= k; j++) {
  24.                     dp[i][j] = dp[i - 1][j] + a[i];
  25.                     if (i >= 3) {
  26.                         dp[i][j] = Math.max(dp[i][j], dp[i - 3][j - 1] + 3);
  27.                     }
  28.                 }
  29.             }
  30.             int res = 0;
  31.             for (int j = 0; j <= k; j++) {
  32.                 res = Math.max(res, dp[n][j]);
  33.             }
  34.             out.println(res);
  35.         }

  36.     }
  37. }
复制代码

评分

参与人数 2大米 +12 收起 理由
14417335 + 10 没亲测。但是看了两个forloop(N, K)应该不.
Neroldy + 2 很有用的信息!

查看全部评分

回复

使用道具 举报

seeker丶 2019-6-18 09:47:43 | 只看该作者
全局:
来贴代码了,昨天睡觉的时候优化到了O(n),其实证明链表里每次断裂后不会产生新的连续3个S(或者1,2个s,如果当前在检查相应的长度)即可,每遍都从前往后扫一遍就可以啦。

  1. #include <iostream>
  2. #include <vector>
  3. #include <string>
  4. #include <iterator>
  5. #include <algorithm>
  6. #include <list>
  7. using namespace std;

  8. int solve(string s,int n,int k)
  9. {
  10.     list<int> l;
  11.     int res = 0;
  12.     for(auto i:s){
  13.         if(i=='.') // . converts to 1 and S converts to 0
  14.         {
  15.             res++;
  16.             l.push_back(0);
  17.         }else
  18.             l.push_back(1);
  19.     }
  20.     for(int curCheck = 3; curCheck>=1 && k>0; curCheck--)
  21.     {
  22.         if(l.size()<3)
  23.             break;
  24.         auto i=l.begin(), j=i;
  25.         int windowSum = *(i++);
  26.         windowSum += *(i++);
  27.         while(i!=l.end())
  28.         {
  29.             windowSum += *(i++);
  30.             if(windowSum == curCheck)
  31.             {
  32.                 res += curCheck;
  33.                 k--;
  34.                 if(k == 0)
  35.                     break;
  36.                 l.erase(j++);
  37.                 l.erase(j++);
  38.                 l.erase(j++);
  39.                 windowSum =0;
  40.                 if(i == l.end())
  41.                     break;
  42.                 windowSum += *(i++);
  43.                 if(i == l.end())
  44.                     break;
  45.                 windowSum += *(i++);
  46.                 continue;
  47.             }
  48.             windowSum -= *(j++);
  49.         }
  50.     }
  51.    
  52.     return res;
  53. }

  54. int main(int argc, const char * argv[]) {
  55.     cout<<solve("SS.S...S....SS..S..S.S.S...SS...SSS..SS.SS.SSSSS...S.S.S...S......S",67,4)<<endl;
  56.     cout<<solve(".S.....SSSSS.SSSS...SS.SSS.SS",29,2)<<endl;
  57.     cout<<solve(".S...SSS.SSS..SSSS.SSSSS.SS.S.SS.SS.SSSSSSS.SS...SS.S.SSS.SS.S.SS..S..S.SSS.SS.",79,6)<<endl;
  58.     cout<<solve("SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.",276,23)<<endl;
  59.     return 0;
  60. }
复制代码

评分

参与人数 1大米 +2 收起 理由
Neroldy + 2 给你点个赞!

查看全部评分

回复

使用道具 举报

推荐
Gary.W 2019-6-18 04:35:52 | 只看该作者
全局:
用heap 可以优化到O(nlogk)
基本思路就是如果对于一个全为S开头的子串 "S......S", 必然是先把从最左边(或最右边)开始把若干个S(<=3) 变为 . 有些位置可以把转化3个S, 有些位置只能转化1或2个S, 我们总共就只能做K 次操作。 所以就选top k 个位置。 太懒了,就先贴一个nlogn 的代码吧, 如果限制heap的size为K, 那么负责度就是 nlogk.

  1. #include <iostream>
  2. #include <queue>
  3. #include <string>

  4. int solve(string s, int k) {
  5.     priority_queue<int> q;
  6.     int idx = 0;
  7.     int ret = 0;
  8.     while(idx < s.size()) {
  9.         if(s[idx] == '.') {
  10.             ret++;
  11.             idx++;
  12.         }
  13.         else {
  14.             int len = 0;
  15.             while(idx + len < s.size() && s[idx + len] == 'S' && len <= 2)
  16.                 len++;
  17.             q.push(len);
  18.             idx += len;
  19.         }
  20.     }
  21.     while(k != 0 && !q.empty()) {
  22.         ret += q.top();
  23.         q.pop();
  24.         k--;
  25.     }
  26.     return ret;
  27. };

  28. string t = "SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.";
  29. int main() {
  30.     cout << solve(t, 23) << endl;
  31.     return 0;
  32. }
复制代码

评分

参与人数 1大米 +2 收起 理由
Neroldy + 2 给你点个赞!

查看全部评分

回复

使用道具 举报

🔗
 楼主| Neroldy 2019-6-15 23:48:22 | 只看该作者
全局:
seeker丶 发表于 2019-6-15 21:57
分享我的一个复杂度是O(NK)的贪心法。用链表管理数组A,每次贪心地从左到右看是否有连续三个S。如果有则都 ...

感谢讨论,干货满满。
我需要再思考一下。
同学说可以用dp做,复杂度差不多也是O(NK),没想到还可以用链表做,学习了。
回复

使用道具 举报

全局:
Neroldy 发表于 2019/06/15 23:48:22


感谢讨论,干货满满。
我需要再思考一下。
同学说可以用dp做,复杂度差不多也是O(NK),没想到还可以用链表做,学习了。

如果知道了dp的做法麻烦分享一下!

评分

参与人数 1大米 +2 收起 理由
Neroldy + 2 老哥能不能分享一下你链表的代码?

查看全部评分

回复

使用道具 举报

🔗
 楼主| Neroldy 2019-6-17 13:36:23 | 只看该作者
全局:
zjck1995 发表于 2019-6-16 23:15
[mw_shl_code=java,true]public class Main {
    public static void main(String[] args) {
        In ...

不过兄弟这个代码不对吧?

补充内容 (2019-6-17 13:57):
妹的,我智障了,几个大数据有点问题。还请忽略。
回复

使用道具 举报

🔗
 楼主| Neroldy 2019-6-17 13:58:24 | 只看该作者
全局:
seeker丶 发表于 2019-6-16 10:22
如果知道了dp的做法麻烦分享一下!

我的锅,大数据你会发现字符串没有N那么长,复制的时候出了问题。
dp解法你看楼下兄弟的,比我同学的那个还简洁。
回复

使用道具 举报

🔗
 楼主| Neroldy 2019-6-17 14:05:03 | 只看该作者
全局:
seeker丶 发表于 2019-6-15 21:57
分享我的一个复杂度是O(NK)的贪心法。用链表管理数组A,每次贪心地从左到右看是否有连续三个S。如果有则都 ...

别的case因为太长,复制的时候出了错,所以只有你测的4个case是争取的。抱歉。
回复

使用道具 举报

本楼:
全局:
楼主牛逼!!!
回复

使用道具 举报

🔗
337845818 2019-6-18 00:41:08 | 只看该作者
全局:
https://leetcode.com/problems/max-consecutive-ones-iii/

补充内容 (2019-6-18 00:45):
类似的想法, 找at most k 'S'. 但是你这里可以一下改1, 2, 3个S. 所以如果是[1 - 3]个S连一块算一个S. O[n]的解法了

补充内容 (2019-6-18 00:52):
看了看题似乎理解错了.. 只是单纯的找最多的点吗..? 容我测一下

补充内容 (2019-6-18 01:09):
https://paste.ubuntu.com/p/SDkjFnqM8S/
能过你说的前三个跟倒数第二个测试.. 这题是谁理解错了

评分

参与人数 1大米 +2 收起 理由
Neroldy + 2 给你点个赞!

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表