AcWing 278. 数字组合【dp光芒初绽时】

dp么,轻松化解~

278. 数字组合​​​​​​

给定 N 个正整数 A1,A2,…,AN从中选出若干个数,使它们的和为 M,求有多少种选择方案。

输入格式

第一行包含两个整数 N 和 M。

第二行包含 N 个整数,表示 A1,A2,…,AN

输出格式

包含一个整数,表示可选方案数。

数据范围

1≤N≤100
1≤M≤10000
1≤Ai≤1000
答案保证在 int 范围内。

输入样例:
4 4
1 1 2 2
输出样例:
3

    现将题目转化一下,我们可以把要求的和看为容量为m的背包,每一个数字看做货物的重量,那么问题就转化为求刚好装满m个背包的可能情况。成功转化成01背包问题。

注意 这里和背包问题不同的是,这个题要求恰好装满,而且求的是所有可能的情况。这里我们要想一想怎么表示出这两个条件。

法一,记忆化递归

递归函数需要的参数是index (当前正在处理数字的下标)rest (距离目标和还剩下的差)

递归的终止条件就是搜索到最后一个数 的时候,如果这个时候 已经选择的数字之和恰好为m,说明找到了一种情况,返回1,否则返回0;

函数主体处理,我拿到一个数字,我可以选(容量足够前提下),也可以不选,我的总方案数就是二者之和。

这样函数就写完了

但可以想见有大量重复计算,所以我们建立一个数组存一下每次算出的答案,做一个记忆性优化

完整代码如下

package 背包;

import java.math.MathContext;
import java.util.Arrays;
import java.util.Scanner;

public class _278_数字组合 {
	static  int [][]dp;
	static  int []a=new int [105];
	
	static int n;
	static  int m;
	//static final int N=1010;

public static void main(String[] args) {
	 Scanner sc=new Scanner(System.in);
	 n=sc.nextInt();
	 m=sc.nextInt();
	 dp=new int [n+1][m+1];
	 for(int i=0;i<dp.length;i++)
	 {
		 Arrays.fill(dp[i],-1);
	 }
	 for(int i=0;i<n;i++)
	 {
		 a[i]=sc.nextInt();
	 }
	 int c=dfs(0,m);
	System.out.println(c);}
	
	 
static int dfs(int index,int rest)
{
	if(dp[index][rest]!=-1) return dp[index][rest];
	if(index==n) return rest==0? 1:0;
	int ans=0;
	
	ans+=dfs(index+1,rest);//不选
	if(a[index]<=rest)//能选则选
		//如果是完全背包的话呢?
	ans+=dfs(index+1,rest-a[index]);
	dp[index][rest]=ans;
	return ans;
	//dp[index][rest]=Math.max(ans1, ans2);
//	return Math.max(ans1, ans2);
}
}

下面是dp版本  注意初始化

package 背包;

import java.math.MathContext;
import java.util.Arrays;
import java.util.Scanner;

public class 数字组合dp优化版本 {
//ac拿下

		static  int [][]dp;
		static  int []a=new int [105];
		
		static int n;
		static  int m;
		//static final int N=1010;

	public static void main(String[] args) {
		 Scanner sc=new Scanner(System.in);
		 n=sc.nextInt();
		 m=sc.nextInt();
		 dp=new int [n+1][m+1];
		int N=dp.length;
		int M=dp[0].length;
		
		 for(int i=0;i<n;i++)
		 {
			 a[i]=sc.nextInt();
		 }
		 dp[N-1][0]=1;//初始化,此时必有一种方法
		 
		 for(int index=N-2;index>=0;index--)
		 {
			 for(int rest=0;rest<M;rest++)
			 {
				 int ans=0;
				 ans+=dp[index+1][rest];//不选
					if(a[index]<=rest)//能选则选
						//如果是完全背包的话呢?
					ans+=dp[index+1][rest-a[index]];
					dp[index][rest]=ans;
			 }
		 }
		System.out.println(dp[0][m]);}
		
		
}

	

新人博主坚持更新不易,可否给博主小小的赞呢?欢迎关注,现在关注绝对不亏

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值