这是一个竞赛问题(ACM ICPC South America 2015),它是问题集中最难的。
摘要:给定整数 N 和 K ,计算长度为的 a 的序列数N 由整数 1 ≤ a i ≤ K 组成,根据条件,对于该序列中的任何 x ,必须有一对 i,j 满足 i < j 和 a i = x - 1和 a j = x ,即最后的 x 前面是 x - 1在某个时刻。
示例: N = 1000且 K = 100,解决方案应与265428620模数一致(10 9 + 7)。其他示例和详细信息可在the problem description中找到。
我在我的知识中尝试了一切,但我需要指点知道如何去做。我甚至用蛮力打印了一些列表以找到模式,但我没有成功。
我正在寻找一种算法或公式,让我能够找到解决此问题的正确解决方案。它可以是任何语言。
编辑:
我使用在互联网上找到的公式解决了这个问题(有人解释了这个问题)。然而,仅仅因为我编程了它,并不意味着我理解它,所以问题仍然存在。我的代码在这里(在线评委返回已接受):
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll mod = 1e9+7;
ll memo[5001][5001];
ll dp(int n, int k){
// K can't be greater than N
k = min(n, k);
// if N or K is 1, it means there's only one possible list
if(n <= 1 || k <= 1) return 1;
if(memo[n][k] != -1) return memo[n][k];
ll ans1 = (n-k) * dp(n-1, k-1);
ll ans2 = k * dp(n-1, k);
memo[n][k] = ((ans1 % mod) + (ans2 % mod)) % mod;
return memo[n][k];
}
int main(){
int n, q;
for(int i=0; i<5001; i++)
fill(memo[i], memo[i]+5001, -1);
while(scanf("%d %d", &n, &q) == 2){
for(int i=0; i<q; i++){
int k;
scanf("%d", &k);
printf("%s%lld", i==0? "" : " ", dp(n, k));
}
printf("\n");
}
return 0;
}
最重要的行是递归调用,特别是这些行
ll ans1 = (n-k) * dp(n-1, k-1);
ll ans2 = k * dp(n-1, k);
memo[n][k] = ((ans1 % mod) + (ans2 % mod)) % mod;
答案 0 :(得分:0)
这里我展示了python中问题的强力算法。它适用于小数字,但对于非常大的数字,它需要花费太多时间。对于N = 1000和K = 5,它已经不可行(需要超过100年的时间来计算)(在C中它也应该是不可行的,因为C只比Python快100倍)。所以问题实际上迫使你找到一条捷径。
import itertools
def checkArr(a,K):
for i in range(2,min(K+1,max(a)+1)):
if i-1 not in a:
return False
if i not in a:
return False
if a.index(i-1)>len(a)-1-a[::-1].index(i):
return False
return True
def num_sorted(N,K):
result=0
for a in itertools.product(range(1,K+1), repeat=N):
if checkArr(a,K):
result+=1
return result
num_sorted(3,10)
按预期返回6.