如何解决这个难以组合的问题?

时间:2016-07-24 10:57:05

标签: math combinatorics

这是一个竞赛问题(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;

1 个答案:

答案 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.