相邻元素之差不超过1的可能数组的数量

时间:2019-06-23 10:28:31

标签: c++ arrays algorithm

您知道一个数组在1到m之间有n个整数,并且两个相邻值之间的差最大为1。

鉴于对数组的描述,其中某些值可能是未知的,您的任务是计算与描述匹配的数组的数量。

在输入数组中,未知值将被指定为“ 0”。 给出模数为1e9 + 7的数组的数量

该问题可以分为两部分。一种是未知数介于已知数之间的位置,然后很容易计算数组的数量, 差异只能是0,1或2。不能是3,因为例如2 0 5,则没有值给出正确的数组。输入将始终在未知值之间存在可能的差异。如果差是0、2 0 2,则3个值是可能的;如果差是1、2 0 3,则1和3是唯一可能的值,因此2个值是可能的。对于2的差,只有1个值,所以结果没有变化是可以的。

但是第二个未知数介于未知数之间的情况是困扰我的事情。

#include <bits/stdc++.h>

using namespace std;

const int MOD   = 1e9 + 7;


int main() {

    int n,m;cin>>n>>m;
    vector<int> arr(n);

    for(int i=0;i<n;i++) cin>>arr[i];

    int res=1;

    if(arr[0]==0 && arr[1]!=0)
        res=(res%MOD*3)%MOD;
    if(arr[n-1]==0 && arr[n-2]!=0)
        res=(res%MOD*3)%MOD;

    //for unknowns between knowns
    for(int i=1;i<n-1;i++)
    {
        if(arr[i]==0 && arr[i-1]!=0 && arr[i+1]!=0)
        {
            int val=arr[i+1]-arr[i-1];
            if(val==0)
                res=(res%MOD*3)%MOD;
            else if(val==1)
                res=(res%MOD*2)%MOD;
        }
    }

    //for unknows between unknowns(I don't know how to approach this. Brute force will surely give TLE)
    for(int i=0;i<n;i++)
    {
        int st=i;
        while(i<n && arr[i]==0)
            i++;

        if(arr[i]!=0) i--;

        if(i-st>=1)
        {
            if(st!=0 && i!=n-1)
            {

            }
        }

    }


    cout<<res;


    return 0;
}

对于2 0 2,输出为3([2 1 2] [2 2 2] [2 3 2])

我想知道如何用连续的未知值来解决此问题的第二种情况。

2 个答案:

答案 0 :(得分:2)

  1. 您需要解决的较小问题是:

    • 鉴于firstlast元素,有多少种方法可用于    填充其他元素?

      用不同的词表示:M个术语的多少组合    -1+0+1在那,总共last-first

      我们想要更正式一点

             first + d_0 + d_1 + ... + d_M-1 == last
         ->  d_0 + d_1 + ... + d_M-2 == last-first
      

      这是组合问题。我会留给你找到    正确的公式,仅举一个例子:

         M = 3
         first = 0
         last = 1
      
         solutions:
             0 +0+0+1 == 1 <- 3 different ways to sort the terms
             0 +1-1+1 == 1 <- 3 different ways sort the terms
      

      编写一个函数find_combinations_between(int first,int last,int M);

    • 小问题:    如果仅给出first last,则解决方案就是3^M

  2. 回到原始问题

    一旦您可以解决较小的问题,直接在中间获得具有已知/未知元素的数组即可。

    伪代码

    int calculate( iterator begin, iterator end) {
        int count = 0;
        auto first = begin;
        auto last = begin;
        // find next known element
        while ( last != end && is_empty( last ) ) {
            ++last;
            ++count;
        }
        // there is none
        if ( last == end) return result * power_of_3( count );
        // ..or recurse to the next sub-interval
        return calculate(last,end) * find_combinations_between(first,last,count);
    }
    

    我认为传递迭代器可能会有所帮助。想法是将数组拆分为子间隔,以便始终仅知道第一个和最后一个元素。 power_of_3is_empty仅用于提高可读性,您可以分别用其他调用或== 0替换它。带着一点点的代码,这是为了概述想法,但可能包含错误。

  3. mod 1e7

    仅当算法本身有效时,我才会将 %1e7部分,因此对于该答案,我将其忽略。


PS 我并没有为您提供完整的解决方案,而是建议您改变观点。您的“两个已知之间的一个未知”案例是一个非常特殊的案例,但是您首先需要一个整体策略。如果遵循我的食谱,那么所有复杂性都集中在一个地方:find_combinations_between。最重要的是,在该函数中,您可以专注于组合问题,而元素是否在某个数组中或其他内容无关紧要。

PPS :如前所述,我没有为您提供完整的解决方案。而且我仍然不想给别人一个。以上只是如何以不同的方式解决问题,但是如何实际解决问题却悬而未决。根据您的要求,我将进一步说明如何解决find_combinations_between

一些可视化可能会有所帮助。由于解决方案仅取决于差异last - first,因此我使用first = 0last = D

首先让我们考虑D==M的情况,即我们必须在每个元素上添加+1才能达到正确结果的简单情况:

0
 \
  \
   \
    \
     D

那很容易,对吧?只有一种解决方案。现在,让我们稍微编译一下:如果D == M-1会怎样?然后,我们必须为除一个元素之外的所有元素添加+1。可能的解决方案是

0        0        0         0
 \        \        \        |
  \        \        |        \
   \        |       \         \
   |        \        \         \
   D         D        D         D

即有尽可能多的自由元素可供选择。在另一端,如果要达到正确的总和的唯一方法是在每个元素上添加-1(或者在一个元素上除其他元素之外添加-1),您将遇到另一种情况。

+1-1结合使用以获得正确的解决方案会变得更加复杂。上面的图表仍然可以帮助您确定方向。考虑一下我在上图中没有绘制的所有路径,那么您有一棵树,需要找到从根到给定叶子的可能路径数。

我不知道如何在自己未真正实现的情况下进行更多解释,这意味着开始编写代码,而不是在此处写下答案;)。因此,我将再次使用示例,希望它可以帮助您找到解决方案。

让我们说差last-first是2。然后,对于给定数量的元素,首先需要找到说4,所有可能的增量,即

+1 +1 +0 +0 = 2    // start with minimum number of +1s, ie 2 in this case
+1 +1 +1 -1 = 2    // next has 3x +1
                   // 4x +1 is not possible

接下来,您必须找到所有不同的排列,即

+1 +1 +0 +0   // 4*3 because 
+1 +0 +1 +0   // 4 choices for the first +1
+1 +0 +0 +1   // x3 choices for the second
+0 +1 +1 +0
+0 +1 +0 +1
+0 +0 +1 +1

+1 +1 +1 -1  // 4 different ways to place the -1
....

总共有12+4=16种不同的方式来添加+10-1,以从任意数量的xx+24步骤中进行操作。

答案 1 :(得分:0)

在c ++中使用DP的上述想法的简单实现 如下:

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007;
typedef long long ll;
ll const N = 1e5 + 5, M = 1e2 + 5;
ll n, m;
ll dp[N][M];
ll a[N];

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;

    for (ll i = 1; i <= n; i++)
        cin >> a[i];
    if (a[1] == 0)
        for (int i = 1; i <= m; i++)
            dp[1][i] = 1;
    else
        dp[1][a[1]] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
            if (a[i] == 0 || j == a[i])
                dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + dp[i - 1][j] + dp[i - 1][j + 1]) % mod;
    }
    ll ans = 0;
    for (int i = 1; i <= m; i++)
    {
        ans += dp[n][i];
        ans %= mod;
    }
    cout << ans;
}