编程比赛中的背包变体

x0v*_*x0v 2 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

在这里尝试了这个问题并提出了以下算法:

既然问题说,我们可以选择一个问题,他可以为该问题的分数加倍。

所以,为了选择那个问题,我应用了贪婪的方法,即。

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

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

    3 10 1 2 3 4 3 4

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

  3 
  9
  9 6 5
  8 5 3
Run Code Online (Sandbox Code Playgroud)

蛮力方法会超过时间限制,因为解决 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);
    }
Run Code Online (Sandbox Code Playgroud)

Pau*_*kin 5

为了解决这个问题,我们首先看一下维基百科上关于背包问题的文章,它为常规问题提供了一个动态规划解决方案:

// 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
Run Code Online (Sandbox Code Playgroud)

(正如文章所说,您可以通过使用一维数组 m 而不是二维数组来减少内存使用量)。

现在我们可以用这个想法来解决扩展问题。您可以计算两个表:您可以计算 m0 和 m1,而不是 m。m0[i, w] 存储使用前 i 个项目获得的最大值(在您的情况下)至多 w,不使用双重评分问题。类似地, 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
Run Code Online (Sandbox Code Playgroud)

与常规问题一样,您可以使用两个一维数组而不是两个二维数组来减少内存使用。