【问题标题】:Path of Length N in graph with constraints带约束的图中长度为 N 的路径
【发布时间】:2014-06-09 04:07:08
【问题描述】:

我想在图中找到长度为 N 的路径数,其中顶点可以是任何自然数。但是,只有当两个顶点的乘积小于某个自然数 P 时,两个顶点才连接。如果两个顶点的乘积大于 P,则两个顶点之间没有连接且无法从另一个顶点到达。

我显然可以运行两个嵌套循环 (

【问题讨论】:

标签: algorithm optimization graph sequence dynamic-programming


【解决方案1】:

我同意 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 表实现是不可能的。但是,floor(P/i) 的不同值最多只有 m aj ... bj,其中 floor(P/aj sub>) = floor(P/bj).我们可以使用二进制搜索在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,但这些都不是真正相关的,所以我放弃了它们。

【讨论】:

  • 很好地观察了 floor(P/i) 的可能值。
【解决方案2】:

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

在这种情况下,DP 可以使用相同的方法,使用推理从顶点v 的长度为n 的路径数是所有邻居的长度为n-1 的路径数的总和。顶点i 的邻居是从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) )的所有定向路径数。

【讨论】:

  • 如果顶点很大怎么办?那我们该如何接近呢?
  • 你问的是内存还是速度?所需的内存在 Q 中是线性的,如果使用部分和,则运行时间是 O(Q*N)。 P(i,n) = sum(N(j,n)) for j in 1 to i = P(i-1,n) + N(i,n)。
  • 我很在意时间。 Q 最高可达 10^9
猜你喜欢
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 2023-02-09
  • 2017-11-21
  • 1970-01-01
相关资源
最近更新 更多