您知道一个数组在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])
我想知道如何用连续的未知值来解决此问题的第二种情况。
答案 0 :(得分:2)
您需要解决的较小问题是:
鉴于first
和last
元素,有多少种方法可用于
填充其他元素?
用不同的词表示: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
。
回到原始问题
一旦您可以解决较小的问题,直接在中间获得具有已知/未知元素的数组即可。
伪代码:
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_3
和is_empty
仅用于提高可读性,您可以分别用其他调用或== 0
替换它。带着一点点的代码,这是为了概述想法,但可能包含错误。
mod 1e7
仅当算法本身有效时,我才会将
%1e7
部分,因此对于该答案,我将其忽略。
PS 我并没有为您提供完整的解决方案,而是建议您改变观点。您的“两个已知之间的一个未知”案例是一个非常特殊的案例,但是您首先需要一个整体策略。如果遵循我的食谱,那么所有复杂性都集中在一个地方:find_combinations_between
。最重要的是,在该函数中,您可以专注于组合问题,而元素是否在某个数组中或其他内容无关紧要。
PPS :如前所述,我没有为您提供完整的解决方案。而且我仍然不想给别人一个。以上只是如何以不同的方式解决问题,但是如何实际解决问题却悬而未决。根据您的要求,我将进一步说明如何解决find_combinations_between
。
一些可视化可能会有所帮助。由于解决方案仅取决于差异last - first
,因此我使用first = 0
和last = 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
种不同的方式来添加+1
,0
或-1
,以从任意数量的x
到x+2
在4
步骤中进行操作。
答案 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;
}