母函数法解决选课问题和dp多重背包解法


Java版的
import java.util.*;

class solution
{
  private int T=0;
  private Scanner iner=new Scanner(System.in);
  private int v[]=new int[41];
  private int c[]=new int[41];
  private int s[]=new int[100];
  private int a[]=new int[100];
  private int n,k;

  public int select()
  {
    T=iner.nextInt();
    while(T!=0)
    {
      T--;
      n=iner.nextInt();
      k=iner.nextInt();
      for(int i=0;i<k;++i)
      {
         v[i]=iner.nextInt();
         c[i]=iner.nextInt();
      }
      for(int i=0;i<=n&&i<=v[0]*c[0];i+=v[0])
      {
        a[i]=1;
      }
      for(int i=1;i<k;++i)
      {
        for(int j=0;j<=n;j++)
        {
          for(int x=0;(j+x)<=n&&x<=v[i]*c[i];x+=v[i])
          {
              s[j+x]+=a[j];
          }
        }
        for(int y=0;y<=n;++y)
        {
          a[y]=s[y];
          s[y]=0;
        }
      }
      System.out.println(a[n]); 
      Arrays.fill(a, 0);
      Arrays.fill(s,0);
    }
    return 0;
  }
}

public class Main {
    public static void main(String[] args) {
        solution space = new solution();
        space.select();
    }
}


import java.util.*;

/**
    * 为什么解决不了选课组合问题
    * 但是可以解决最大价值的问题
    * 还有种多重背包问题是在一堆数中能否找到能组合成n容量的物品
    * 而选课问题就是将所有能组合的种数算出来
    * 两者之间有什么区别
    * 母函数用多重背包解决的状态转移方程:dp[0]=1;和dp[j]=(dp[j]+dp[j-k*p[i]])
    * 多重背包解决思路:我们可以将完全背包转化为01背包求解,我们可以把 一个物品拆分成k个物品(w[x]*k<=C),然后按照01背包求解即可.
    * 那么如果价值相同的物品没有区别呢?
    * for (i = 1; i <= m; i++)
    *        for (j = n; j >= a[i]; j--)
    *            for (k = 1; k <= num[i] && (j - k * a[i]) >= 0; k++)   //k用来表示的是数量,将这个物品看成整体去算对于背包容量来说的各种情况一层一层的去推
    *                dp[j] = dp[j] + dp[j - k * a[i]];        
    * 这段代码可以避免相同的物品却表现出不一样;一层一层的去算
    */

class Solution
{
   private int v[];   //价值
   private int c[];   //数量
   //private int w[];   //体积
   private int num;   //物品的种数
   private int k;     //物品的总数
   private int dp[];  //最大价值表
   private int cnt;   //背包的最大容量 
   private Scanner iner=new Scanner(System.in);

   public int Selected()
   {
       dp[0]=1;
    for(int i=0;i<this.k;++i)
    {
        for(int j=this.cnt;j>=this.v[i];--j)
        {
            for(int k=1;k<=this.c[i]&&(j-k*this.v[i])>=0;++k)
            {
                dp[j]=dp[j]+dp[j-k*this.v[i]];
                //dp[j]=Math.max(dp[j], dp[j-this.v[i]]+1);  
            }
              
        }
    }
    return dp[this.cnt];
   }

   public Solution()
   { 
       System.out.print("请输入要修的学分:");
       this.cnt=iner.nextInt();
       System.out.print("请输入课程的种数:");
        this.num=iner.nextInt();
        this.c=new int[this.num+100];
        //this.w=new int[this.num+100];
        this.dp=new int[this.num+100];
        this.v=new int[this.num+100];
       System.out.print("请输入每种课的学分及数量:");
       for(int i=0;i<this.num;++i)
       {
        this.v[i]=iner.nextInt();
        //this.w[i]=iner.nextInt();
        this.c[i]=iner.nextInt();
       }
       this.k=this.num;
       /* for(int i=0;i<this.num;++i)
       {
        while(this.c[i]!=1)
        {
          this.v[k]=this.v[i];
          //this.w[k]=this.w[i];
          this.c[i]--;
          this.c[k]=this.c[i];
          k++;
        }
       } */
   }
}
#include<stdio.h>
#include<string.h>
#include <windows.h>

int c[45],s[45];
int a[8],b[8];
int main()
{
    int T,i,j,t,n,k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&k);
        memset(a,0,sizeof(a));
        memset(s,0,sizeof(s));
        for(i=0;i<k;i++)
        scanf("%d%d",&a[i],&b[i]);
        for(i=0;i<=n&&i<=a[0]*b[0];i+=a[0])
        s[i]=1;      //将第一组数据的系数全部初始化为1
        for(i=1;i<k;i++)                                //i从1开始因为第一次性解决了两项,第二次直接从第三项开始
        {
            for(j=0;j<=n;j++)    //j+=a[i-1]为了减少一些不必要的计算||j<=a[i-1]*b[i-1]
            {
                for(t=0;t+j<=n&&t<=a[i]*b[i];t+=a[i])
                c[j+t]+=s[j];                            //j代表前一项的指数,t代表后一项的指数 c[j+t]+=s[j]表示两者相乘时的系数相加
            }
            for(j=0;j<=n;j++)
            {
                s[j]=c[j];  
                c[j]=0;
            }
        }
        printf("%d
",s[n]);
    }
    system("pause");
    return 0;
} 
原文地址:https://www.cnblogs.com/z2529827226/p/11628846.html