【问题标题】:Removal of billboards from given ones从给定的广告牌中删除
【发布时间】:2012-02-21 11:41:43
【问题描述】:

我遇到了这个问题

ADZEN 是您所在城市非常受欢迎的广告公司。在每一条路上 你可以看到他们的广告牌。最近他们面临着一个 严峻的挑战,名爵路是你最常用、最美的路 城市几乎被广告牌填满了,这有一个 对
的负面影响 自然景观。 应人们的要求,ADZEN 决定拆除部分广告牌 这样一来,站在一起的广告牌不超过 K 个 在道路的任何部分。 您可以假设 MG 路是一条有 N 个广告牌的直线。最初,任何两个相邻点之间没有间隙 广告牌。 ADZEN 的主要收入来自这些广告牌,因此广告牌移除过程必须以这样一种方式完成,即 广告牌 留在最后应该在所有可能的最终配置中提供最大可能的利润。配置的总利润是 出现在其中的所有广告牌的利润值的总和 配置。 给定 N,K 和 N 个广告牌中每个广告牌的利润值,输出剩余的广告牌所能获得的最大利润 给定条件下的广告牌。

输入说明

第一行包含两个空格分隔的整数 N 和 K。然后按照 N 行描述每个广告牌的利润值,即 行包含第 i 个广告牌的利润值。

    Sample Input
    6 2 
    1
    2
    3
    1
    6
    10 

    Sample Output
    21

解释

在给定的输入中,有 6 个广告牌,在此过程之后不应超过 2 个。所以删除第 1 和第 4 广告牌给出配置 _ 2 3 _ 6 10 的利润为 21。 没有其他配置的利润超过 21。所以答案是 21。

    Constraints
    1 <= N <= 1,00,000(10^5)
    1 <= K <= N
    0 <= profit value of any billboard <= 2,000,000,000(2*10^9)

我认为我们必须在前 k+1 个板上选择最低成本板,然后重复相同的操作直到最后,但这并没有给出正确的答案 对于所有情况。 我尽我所知,但无法找到解决方案。 如果有人有想法,请分享您的想法。

【问题讨论】:

标签: algorithm


【解决方案1】:

这是一个典型的 DP 问题。假设 P(n,k) 是在道路上的位置 n 之前有 k 个广告牌的最大利润。那么你有以下公式:

 P(n,k) = max(P(n-1,k), P(n-1,k-1) + C(n))
 P(i,0) = 0 for i = 0..n

其中 c(n) 是把第 n 个广告牌放在路上的利润。使用该公式自下而上计算 P(n, k),您将在 O(nk) 时间内得到解决方案。

我会让你弄清楚为什么这个公式成立。

编辑

该死,我看错了问题。

仍然是DP问题,只是公式不同。假设 P(v,i) 表示最后一组广告牌大小为 i 的点 v 处的最大利润。 那么 P(v,i) 可以用以下公式来描述:

P(v,i) = P(v-1,i-1) + C(v) if i > 0 
P(v,0) = max(P(v-1,i) for i = 0..min(k, v)) 
P(0,0) = 0

你需要找到max(P(n,i) for i = 0..k))

【讨论】:

  • 我无法理解这个“k 广告牌到第 n 位”,请您详细说明一下。这里的 k 是什么? .是否输入中给出的值(即)最多 k 个广告牌是连续的
  • 想象道路是一个整数轴。在每个点上,您都可以让广告牌站立或不站立。所以“k个广告牌直到位置n”意味着你有k个广告牌放在一些位置直到位置n。 IE。位置 5 之前的 3 个广告牌表示:BB_B_B_B_BBBB__ 等...
  • 你将如何检查最多 k 是否被分组。还提到问题中给出的 k 值和你提到的是否相同
  • 有没有办法优化你的解决方案?使用这个解决方案只能解决 10 个案例中的 4 个,其余的会出现超出时间限制的错误。我不想为此提出一个新问题。
  • @AccessDenied 抱歉回复晚了。下班后我看看我能做什么
【解决方案2】:

这个问题是 www.interviewstreet.com 上发布的挑战之一......

我很高兴地说我最近搞定了,但不是很满意,想看看是否有更好的方法。

上面soulcheck的DP解决方案很简单,但由于K可以和N一样大,因此无法完全解决这个问题,这意味着运行时和空间的DP复杂度都是O(NK)。

另一种解决方案是进行分支定界,跟踪迄今为止的最佳总和,并在某个级别修剪递归,即如果 currSumSoFar + SUM(a[currIndex..n))

上面的分支定界被测试人员接受,除了 2 个测试用例。 幸运的是,我注意到这 2 个测试用例使用了小 K(在我的例子中,K

【讨论】:

  • DP 技术足够快。我得到了 std::bad_alloc ,:(。
【解决方案3】:

soulcheck 的(第二个)DP 解决方案原则上是正确的。您可以使用这些观察结果进行两项改进:

1) 没有必要分配整个 DP 表。您一次只能查看两行。

2) 对于每一行(P(v, i) 中的 v),您只对最大增加最大值的 i 感兴趣,这比前一行中保持最大值的每个 i 多一个.另外,i = 1,否则你永远不会考虑空白。

【讨论】:

    【解决方案4】:

    我在 C++ 中使用 O(nlogk) 中的 DP 对其进行了编码。 想法是为给定位置维护具有下 k 个值的多重集。该多重集通常在中间处理中具有 k 个值。每次移动一个元素并推送一个新元素。艺术是如何维护这个列表来获得利润[i] + answer[i+2]。更多关于片场的细节:

    /* * 观察 1:第 i 个状态取决于接下来的 k 个状态 i+2....i+2+k * 我们在这些状态中最大化添加“累积”总和 * * 假设我们有状态 i+1 的数字列表,即 {profit + state solution} 的列表,如果第 i 个解决方案如何获得状态 * * 假设我们有以下数据 k = 3 * * 指数:0 1 2 3 4 * 利润:1 3 2 4 2 * 解决方案: ? ? 5 3 1 * * [1] 的答案 = max(3+3, 5+1, 9+0) = 9 * * 指数:0 1 2 3 4 * 利润:1 3 2 4 2 * 解决方案: ? 9 5 3 1 * * 让我们使用 [1] 的集合找到 [0] 的答案。 * * 首先,应删除最后一个条目。那么我们有 (3+3, 5+1) * * 现在我们应该添加 1+5,但是条目应该增加 1 * (1+5, 4+3, 6+1) -> 然后找到最大值。 * * 我们可以用其他方式而不是处理列表来做。是的,我们只是给所有元素加 1 * * 答案等于:1 + max(1-1+5, 3+3, 5+1) * */
    ll dp()
    {
    multiset<ll, greater<ll> > set;
    
    mem[n-1] = profit[n-1];
    
    ll sumSoFar = 0;
    
    lpd(i, n-2, 0)
    {
        if(sz(set) == k)
            set.erase(set.find(added[i+k]));
    
        if(i+2 < n)
        {
            added[i] = mem[i+2] - sumSoFar;
            set.insert(added[i]);
            sumSoFar += profit[i];
        }
    
        if(n-i <= k)
            mem[i] = profit[i] + mem[i+1];
        else 
            mem[i] = max(mem[i+1], *set.begin()+sumSoFar);
    }
    
    return mem[0];
     }
    

    【讨论】:

      【解决方案5】:

      这看起来像一个线性规划问题。这个问题应该是线性的,但是要求不超过 K 个相邻的广告牌。

      查看维基百科了解一般处理方法:http://en.wikipedia.org/wiki/Linear_programming

      访问您的大学图书馆以查找有关该主题的好教科书。

      有很多很多的库可以帮助进行线性编程,所以我建议你不要尝试从头开始编写算法。以下是与 Python 相关的列表:http://wiki.python.org/moin/NumericAndScientific/Libraries

      【讨论】:

      • 从问题中可以明显看出它是“从头开始编码”的。它看起来像是在 Java 编程挑战中出现的东西。
      • @LastCoder:也许吧。无论如何,这是你不应该从头开始编码的极致(几乎与加密代码相提并论),因为它很容易出错。
      • 不,您不能将约束表述为规范形式,因此,它不是 LP。
      • @IvorPrebeg 您是否只是忽略了使您的 cmets 无效的部分答案?
      【解决方案6】:

      P[i](其中i=1..n)成为广告牌1..i如果我们移除广告牌i的最大利润。知道所有 P[i] 来计算答案是微不足道的。计算P[i]的基线算法如下:

      for i=1,N
      {
        P[i]=-infinity;
        for j = max(1,i-k-1)..i-1
        {
          P[i] = max( P[i], P[j] + C[j+1]+..+C[i-1] );
        }
      }
      

      现在这个想法可以让我们加快速度。假设我们只有1i 两种不同的有效广告牌配置,我们将这些配置称为X1X2。如果在配置X1profit(X1) &gt;= profit(X2) 中删除了广告牌i,那么我们应该始终更喜欢配置X1 用于广告牌1..iprofit() 我的意思是仅从广告牌1..i 获得的利润,而不管配置如何) i+1..n)。这一点很重要。


      我们引入了一个元组的双向链表{idx,d}{{idx1,d1}, {idx2,d2}, ..., {idxN,dN}}

      • p-&gt;idx 是最后一个被移除的广告牌的索引。 p-&gt;idx 随着我们浏览列表而增加:p-&gt;idx &lt; p-&gt;next-&gt;idx
      • 如果p 不是列表中的最后一个元素,p-&gt;d 是元素 (C[p-&gt;idx]+C[p-&gt;idx+1]+..+C[p-&gt;next-&gt;idx-1]) 的总和。否则,它是直到当前位置的元素总和减一:(C[p-&gt;idx]+C[p-&gt;idx+1]+..+C[i-1])

      算法如下:


      P[1] = 0;
      list.AddToEnd( {idx=0, d=C[0]} );
      // sum of elements starting from the index at top of the list
      sum = C[0]; // C[list->begin()->idx]+C[list->begin()->idx+1]+...+C[i-1]
      for i=2..N
      {
        if( i - list->begin()->idx > k + 1 ) // the head of the list is "too far"
        {
          sum = sum - list->begin()->d
          list.RemoveNodeFromBeginning()
        }
        // At this point the list should containt at least the element
        // added on the previous iteration. Calculating P[i].
        P[i] = P[list.begin()->idx] + sum
        // Updating list.end()->d and removing "unnecessary nodes"
        // based on the criterion described above
        list.end()->d = list.end()->d + C[i]
        while(
          (list is not empty) AND
          (P[i] >= P[list.end()->idx] + list.end()->d - C[list.end()->idx]) )
        {
          if( list.size() > 1 )
          {
            list.end()->prev->d += list.end()->d
          }
          list.RemoveNodeFromEnd();
        }
        list.AddToEnd( {idx=i, d=C[i]} );
        sum = sum + C[i]
      }
      

      【讨论】:

        【解决方案7】:
        //shivi..coding is adictive!!
        #include<stdio.h>
        
        long long int arr[100001];
        long long  int sum[100001];
        long long  int including[100001],excluding[100001];
        long long int maxim(long long int a,long long int b)
        {if(a>b) return a;return b;}
        
        int main()
        {
        int N,K;
        scanf("%d%d",&N,&K);
        for(int i=0;i<N;++i)scanf("%lld",&arr[i]);
        
        sum[0]=arr[0];
        including[0]=sum[0];
        excluding[0]=sum[0];
        for(int i=1;i<K;++i)
        {
            sum[i]+=sum[i-1]+arr[i];
            including[i]=sum[i];
            excluding[i]=sum[i];
        }
        
        long long int maxi=0,temp=0;
        for(int i=K;i<N;++i)
        {
            sum[i]+=sum[i-1]+arr[i];
            for(int j=1;j<=K;++j)
            {
                temp=sum[i]-sum[i-j];
                if(i-j-1>=0)
                temp+=including[i-j-1];
                if(temp>maxi)maxi=temp;
            }
            including[i]=maxi;
            excluding[i]=including[i-1];
        }
        printf("%lld",maxim(including[N-1],excluding[N-1]));
        }
        
        //here is the code...passing all but 1 test case :) comment improvements...simple DP
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多