帮助解决DP问题

时间:2011-08-22 16:19:43

标签: c algorithm dynamic intervals

我正在尝试解决SPOJ(link)中的一个问题,这可以简单地描述如下:给定n个区间,每个区间都有一个整数的开头和结尾,并给出结束时的最大时间(让我们看看)称之为max_end),找到多少种方法可以选择一组覆盖1 ... max_end的区间。间隔可能重叠。我试过DP;首先按结束时间排序,然后dp [i]是一对,其中dp [i] .first是覆盖1 ... end [i] 最后使用间隔i所需的最小间隔数和dp [i] .second是执行此操作的方式的数量。这是我的主要DP循环:

for( int i = 1; i < n; i ++ ) {
    for( int j = 0; j < i; j ++ ) {
        if( ! ( x[ j ].end >= x[ i ].start - 1 ) )
            continue;
        if( dp[ j ].first + 1 < dp[ i ].first ) {
            dp[ i ].first = dp[ j ].first + 1;
            dp[ i ].second = dp[ j ].second;
        }
        else if( dp[ j ].first + 1 == dp[ i ].first ) {
            dp[ i ].second += dp[ j ].second;
        }
    }
}

不幸的是,它没有用。有人可以告诉我哪里有错误吗?提前致谢! :)

1 个答案:

答案 0 :(得分:1)

我不确定我的解决方案是什么,但我描述了我的AC解决方案:

我正在使用记忆功能,但您可以使用非重复DP重新编写它。

假设我们在数组中有间隔

配对[100]; 哪里 a [i] .first是间隔开始,a [i] .second是间隔结束。

首先按 begin 对此数组进行排序(使用默认对比较器的stl排序算法的默认行为)。

现在想象一下,我们从头到尾一个接一个地“放置”间隔。

让f(int x,int prev)返回完成填充的方式,如果当前最后一个间隔是x而前一个是'prev'。

我们将按如下方式计算:

int f(int x, int prev) {
  // if already calculated dp[x][prev], return it. Otherwise, calculate it
  if (dp[x][prev] != -1) {
    return dp[x][prev];
  }
  if (a[x].second == m) {
    return dp[x][prev] = 1; // it means - X is last interval in day
  }
  else {
    dp[x][prev] = 0;
    for (int i = x + 1; i < n; ++i) { // try to select next interval
      if (a[i].first <= a[x].second && // there must be not empty space after x interval
          a[i].second > a[x].second && // if this is false, the set won't be minimal - i interval is useless
          a[i].first > a[x].first && // if this is false, the set won't be minimal, x interval is useless
          a[prev].second < a[i].first) { // if this is false, the set won't be minimal, x interval is useless.  
        dp[x][prev] = (dp[x][prev] + f(i, x)) % 100000000;
      }
    }
  }
  return dp[x][prev];
}

之后我们需要为每对间隔调用此函数,首先从0开始,第二个与第一个连接:

for (int i = 0; i < n; ++i) {
  if (a[i].first == 0) {
     for (int j = i + 1; j < n; ++j) {
        if (a[j].first > 0 && // we don't need to start at 0 - in this case either i or j will be useless
            a[j].first <= a[i].second && // there must be no space after i interval
            a[j].second > a[i].second) { // in opposite case j will be useless
           res = (res + f(j, i)) % 100000000;
        }
     }
     // also we need to check the case when we use only one interval:
     if (a[i].second == m) {
        res = (res + 1) % 100000000;
     }
  }
}

之后我们只需要打印res。