编程竞赛中的背包变异

时间:2014-05-18 06:36:14

标签: arrays algorithm knapsack-problem

问题:

考试由N个问题组成。 N个问题的标记分别为m1,m2,m3,... mN。 Jam正在参加考试,他希望最大化他的分数。 但是他需要一些时间来解决每个问题。他解决问题所花费的时间分别为t1,t2,t3,... tN。 考试总共持续时间T. 但Jam的老师很聪明,她知道Jam会找到一种获得最大分数的方法。所以,为了混淆Jam,她还为他提出了奖金 - 该提议是Jam可以选择一个问题,他可以将该问题的分数加倍。 现在,Jam确实很困惑。帮助他找出他可以获得的最大分数。

约束

1·; = N< = 1000

1·; = T< = 10000

1·= MI< = 100000

1·= TI< = 10000

我尝试了这个问题here并提出了以下算法:

由于问题所在,我们可以选择一个问题,他可以将该问题的分数加倍。

因此,为了选择那个问题,我应用了贪婪方法,即

  1. 应该选择具有较大(标记/时间)比率的问题,他可以将该问题的分数加倍。
  2. 如果该比率也相同,则应选择具有较大标记的问题。

    就我所理解的问题而言,其余的都是简单的背包。 我尝试了以下实现,但得到了错误的答案。 对于给定的测试用例,我的代码提供了正确的输出

    3   10   1 2 3   4 3 4

  3. 我已经尝试了在评论部分给出的这个测试用例,我的代码给出16作为输出,但答案应该是18

      3 
      9
      9 6 5
      8 5 3
    

    蛮力方法会超出时间限制,因为解决N背包的复杂性O(nW)会给出O(n ^ 2 W)的总体时间复杂度 我认为可以为这个问题开发一个更具体的算法。 有没有人有比这更好的想法?

    谢谢

        #include<iostream>
        #include<cstdio>
        using namespace std;
        int T[2][10002];
        int a[1000002],b[100002];
        float c[100002];
        int knapSack(int W,int val[],int wgt[],int n)
        {
        int i,j;
    
        for(i=0;i<n+1;i++)
        {
            if(i%2==0)
            {
                for(j=0;j<W+1;j++)
                {
                if(i==0 || j==0)
                T[0][j]=0;
                else if(wgt[i-1]<=j)
                T[0][j]=max(val[i-1]+T[1][j-wgt[i-1]],T[1][j]);
                else
                T[0][j]=T[1][j];
                }
            }
            else
            {
                for(j=0;j<W+1;j++)
                {
                if(i==0 || j==0)
                T[1][j]=0;
                else if(wgt[i-1]<=j)
                T[1][j]=max(val[i-1]+T[0][j-wgt[i-1]],T[0][j]);
                else
                T[1][j]=T[0][j];
                }
            }
        }
        if(n%2!=0)
            return T[1][W];
        else
            return T[0][W];
        }
    
    
        int main()
        {
        int n,i,j,index,t,mintime,maxval;
        float maxr;
        cin>>n;
        cin>>t;
        for(i=0;i<n;i++)
            cin>>a[i];
    
        for(i=0;i<n;i++)
            cin>>b[i];
    
        maxr=0;
        index=0;
        mintime=b[0];
        maxval=a[0];
    
        for(i=0;i<n;i++)
            {
                c[i]=(float)a[i]/b[i];  
                    if(c[i]==maxr && b[i]<=t)
                    {
                        if(a[i]>=maxval)
                        {
                        maxval=a[i];
                        mintime=b[i];
                        index=i;
                        }
                    }   
                    else if(c[i]>maxr && b[i]<=t)
                    {
                    maxr=c[i];
                    maxval=a[i];
                    mintime=b[i];
                    index=i;
                    }
    
            }
    
        a[index]=a[index]*2;
        int xx=knapSack(t,a,b,n);
        printf("%d\n",xx);
        }
    

2 个答案:

答案 0 :(得分:4)

要解决这个问题,我们先来看the wikipedia article on the Knapsack problem,为常规问题提供动态编程解决方案:

// Input:
// Values (stored in array v)
// Weights (stored in array w)
// Number of distinct items (n)
// Knapsack capacity (W)
for j from 0 to W do
  m[0, j] := 0
end for 
for i from 1 to n do
  for j from 0 to W do
    if w[i] <= j then
      m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
    else
      m[i, j] := m[i-1, j]
    end if
  end for
end for

(正如文章所说,你可以通过使用1-d数组m而不是2-d数组来减少内存使用量。)

现在我们可以用这个想法来解决扩展问题。您可以计算两个表:而不是m,您可以计算m0和m1。 m0 [i,w]存储使用带有权重(在您的情况下是时间)最多w,的第i个项目获得的最大值,而不使用双重评分问题。类似地,m1存储使用第一个i项目获得的最大值,其中权重(在您的情况下为时间)最多为w,使用双重评分问题。

更新规则更改为:

if w[i] <= j then
    m0[i, j] := max(m0[i-1, j], m0[i-1, j-w[i]] + v[i])
    m1[i, j] := max(m1[i-1, j], m1[i-1, j-w[i]] + v[i], m0[i-1, j-w[i]] + 2 * v[i])
else
    m0[i, j] = m0[i-1, j]
    m1[i, j] = m1[i-1, j]
end if

与常规问题一样,您可以使用两个1-d数组而不是两个2-d数组来减少内存使用量。

答案 1 :(得分:-1)