• HackerRank# Wet Shark and Two Subsequences


    原题地址

    对于给定的两个约束条件,可以通过联立方程组直接解出子序列A的和和子序列B的和,即sum(A) = (r + s) / 2,sum(B) = (r - s) / 2,假设|A|=|B|=n

    所以问题变成了,在一个数组中求长度为n且子序列和为sum(A)或sum(B)有多少个。

    假设count(n, s)表示长度为n且子序列和为s有多少个,则要求的是count(n, sum(A)) * count(n, sum(B)),其中1<=n<=m

    或者通俗来说就是m个k-sum问题

    求k-sum,如果用搜索的方法,时间复杂度大概是n^k量级(可以优化到n^(k - 1) + nlogn),何况现在要求的是m个k-sum问题,即使排序+剪枝优化肯定也会超时(别问我是怎么知道的

    所以只能选择动归,因为每次求k-sum的过程存在大量重复计算。

    令f[i][j][k]表示从第i个元素开始,子序列长度为j,子序列和为k,这样的子序列有多少个

    那么有地推公式:f[i][j][k] = f[i+1][j - 1][k - a[i]] + f[i + 1][j][k]

    这个公式其实挺straightforward,跟背包问题是一样的。

    由于sum(A)和sum(B)最大不过2000,所以k的范围是2000,另外i和j的范围分别是m的范围100,所以如果不做任何状态压缩,占用空间大概是2000*100*100*4*8约为640MB(用int存储),显然要爆。所以还必须状态压缩。

    分析地推公式,明显压缩i那维,而且不难看出j和k必须从后向前推。还是跟背包问题一样。

    代码:

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <vector>
     4 #include <iostream>
     5 #include <algorithm>
     6 #include <cstring>
     7 using namespace std;
     8 
     9 #define MAX_SIZE 128
    10 #define MAX_SUM 2048
    11 #define MOD 1000000007
    12 
    13 long long a[MAX_SIZE];
    14 int m, r, s;
    15 long long f[MAX_SIZE][MAX_SUM];
    16 
    17 int main() {
    18     /* Enter your code here. Read input from STDIN. Print output to STDOUT */
    19     int sa, sb;
    20     long long res = 0;
    21 
    22     cin >> m >> r >> s;
    23     sa = (r + s) / 2;
    24     sb = (r - s) / 2;
    25     for (int i = 0; i < m; i++)
    26         cin >> a[i];
    27 
    28     memset(f, 0, sizeof(f));
    29     f[0][0] = 1;
    30     for (int i = m - 1; i >= 0; i--) {
    31         for (int j = m; j >= 1; j--) {
    32             for (int k = 2000; k >= 0; k--) {
    33                 if (k >= a[i])
    34                     f[j][k] = (f[j][k] + f[j - 1][k - a[i]]) % MOD;
    35             }
    36         }
    37     }
    38 
    39     for (int i = 1; i <= m; i++)
    40         res = (res + (f[i][sa] * f[i][sb]) % MOD) % MOD;
    41     
    42     cout << res << endl;
    43     return 0;
    44 }

    第一次用的int,结果提交以后有些case是WA,考虑到DP问题基本上不会出现WA的情况,所以断定应该是数据溢出了,换成long long果然就没问题了。

  • 相关阅读:
    Syn Bot /OSCOVA 基础教程(2)
    Syn Bot /OSCOVA 介绍(1)
    如何访问阿里云内网数据库
    Winform项目中的Settings.settings与App.config
    WinForm项目开发傻瓜教程
    C++读取BMP文件
    boost异步tcp通信技术练习
    lex/flex 学习笔记 一
    流数据解析中高位地址转换的性能分析
    bash随笔
  • 原文地址:https://www.cnblogs.com/boring09/p/4464469.html
Copyright © 2020-2023  润新知