长度N的路径在有约束的图表中

时间:2014-04-24 03:31:13

标签: algorithm optimization graph sequence dynamic-programming

我想在图中找到长度为N的路径数,其中顶点可以是任何自然数。但是,只有当两个顶点的乘积小于某个自然数P时,才连接两个顶点。如果两个顶点的乘积大于P,那么它们之间没有连接,并且不能相互到达。

我显然可以运行两个嵌套循环(< = P)并创建一个邻接矩阵,但P可能非常大,这种方法会非常慢。谁能想到一些解决问题的最佳方法?我们可以使用动态编程解决它吗?

2 个答案:

答案 0 :(得分:2)

我同意Ante的复发,尽管我使用的是略微简化的版本。请注意,我使用字母P来命名最大产品,因为它在the original problem statement中使用:

f(1,x) = 1
f(i,x) = sum(f(i-1, y) for y in {1, ..., floor(P/x)})

f(i,x)是以 x 结尾的长度 i 的序列数。这个问题的答案是 f(n + 1,1)

当然,由于在此任务中P可以达到10 ^ 9,因此使用DP表的直接实现是不可能的。但是,只有m <1。可能有70000个不同的 floor(P / i)值。所以让我们找到最大的段 a j ... b j ,其中 floor(P / a j )= floor(P / b j 。我们可以使用二进制搜索在 O(段数* log P)中找到这些段。

想象一下 f 的完整DP表。由于 floor(P / x)只有 m 不同的值, f 的每一行都包含 m 具有相同值的连续范围。

因此,让我们计算压缩 DP表,其中我们将行表示为(长度,值)对的列表。我们从f(1)= [(P,1)]开始,我们可以通过按递增顺序处理段并计算存储在f(i)中的长度的前缀和来从f(i)计算f(i + 1)

我实现此方法的总运行时间是 O(m(log P + n))。这是我使用的代码:

using ll=long long;
const int mod = 1000000007;
void add(int& x, ll y) { x = (x+y)%mod; }
int main() {
    int n, P;
    cin >> n >> P;
    int x = 1;
    vector<pair<int,int>> segments;
    while(x <= P) {
        int y = x+1, hi = P+1;
        while(y<hi) {
            int mid = (y+hi)/2;
            if (P/mid < P/x) hi=mid;
            else y=mid+1;
        }
        segments.push_back(make_pair(P/x, y-x));
        x = y;
    }
    reverse(begin(segments), end(segments));
    vector<pair<int,int>> dp;
    dp.push_back(make_pair(P,1));
    for (int i = 1; i <= n; ++i) {
        int j = 0;
        int sum_smaller = 0, cnt_smaller = 0;
        vector<pair<int,int>> dp2;
        for (auto it : segments) {
            int value = it.first, cnt = it.second;
            while (cnt_smaller + dp[j].first <= value) {
                cnt_smaller += dp[j].first;
                add(sum_smaller,(ll)dp[j].first*dp[j].second);
                j++;
            }
            int pref_sum = sum_smaller;
            if (value > cnt_smaller)
                add(pref_sum, (ll)(value - cnt_smaller)*dp[j].second);
            dp2.push_back(make_pair(cnt, pref_sum));
        }
        dp = dp2;
        reverse(begin(dp),end(dp));
    }
    cout << dp[0].second << endl;
}

我需要通过处理数组来进行一些微优化以获得AC,但这些并不是真正相关的,所以我把它们留下了。

答案 1 :(得分:0)

如果顶点数小于邻接矩阵(A)可以提供帮助。由于A^N中的元素总和是不同路径的数量,因此路径是定向的。如果不是路径数i元素之和/ 2.这是因为元素(i,j)表示从顶点i到顶点j的路径数。

在这种情况下,DP可以使用相同的方法,使用推理,来自顶点n的长度v的路径数是所有路径长度n-1的总和它的邻居。顶点i的Neigbours是从1floor(Q/i)的顶点。有了它,我们可以构造函数N(vertex, length),它表示给定长度给定顶点的路径数:

N(i, 1) = floor(Q/i),
N(i, n) = sum( N(j, n-1) for j in {1, ..., floor(Q/i)}.

所有定向路径的长度为sum( N(i,N) )