输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数A[i](32位整数),以空格隔开。
输出所求的方案数
5 15 5 5 10 2 3
4
#include <cstdio>
#include <algorithm>
#include <cstring>
long long dp[1002][1002];
int main()
{
int n;
int A[1002];
int sum;
while (scanf("%d %d", &n, &sum) != EOF)
{
for (int i = 0; i<n; i++)
scanf("%d", A + i);
for (int i = 0; i <= sum; i++)
dp[0][i] = 0;
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= sum; j++)
{
dp[i][j] = dp[i - 1][j];
if (j >= A[i-1])
dp[i][j] += dp[i - 1][j - A[i-1]];
}
}
printf("%lld\n", dp[n][sum]);
}
return 0;
} import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int sum = in.nextInt();
int[] value = new int[n];
long[] dp = new long[sum + 1];
dp[0] = 1;
for (int i = 0; i < n; i++) {
value[i] = in.nextInt();
for (int j = sum; j >= 0; j--) {
if (j >= value[i]) {
dp[j] += dp[j - value[i]];
}
}
}
System.out.println(dp[sum]);
}
}
/*
给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
解:此题使用递归的遍历方法也可以解决,但是会超时
dp解决:
以每个物品作为横轴,以背包容量作为纵轴
0 1 2 3 4 5 6..........
0 1 0 0 0 0 0 0..........
5 1 0 0 0 0 1 0
其中1表示前n件物品放入容量为M的背包有1种方法,(5,0)表示重量为5的物品放入容量为0的背包的背包有1中方法,即不放入。0表示恰好放满背包的方法为0
当M>weight[i]时,dp[M]=dp[M]+dp[M-weight[i]];意义是:放入物品i和不放入物品i的方法总和
*/
import java.util.*;
public class Main{
public static long bag(int []weight,int n,int sum){
long dp[]=new long[sum+1];
dp[0]=1;
int i,j;
for(i=0;i<n;i++){
for(j=sum;j>=weight[i];j--){
dp[j]=dp[j-weight[i]]+dp[j];
}
}
return dp[sum];
}
public static void main(String args[]){
Scanner s=new Scanner(System.in);
int n=s.nextInt();
int sum=s.nextInt();
int i,j;
int arr[]=new int[n];
for(i=0;i<n;i++){
arr[i]=s.nextInt();
}
System.out.println(bag(arr,n,sum));
}
}
最简单的是一维动态规划 0-1背包问题变种 求方法数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
//常量区
const int maxn = 1e3 + 5;
int A[maxn], n, sum;
long long dp[maxn];
//main函数
int main(){
scanf("%d%d", &n, &sum);
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++) scanf("%d", &A[i]);
dp[0] = 1;
for(int i = 1; i <= n; i++)
for(int j = sum; j >= A[i]; j--)
dp[j] = dp[j] + dp[j - A[i]];
printf("%lld\n", dp[sum]);
return 0;
}
/*
5 15
5 5 3 2 10
ans : 4
*/
n, m = [int(i) for i in input().split()] a = list(map(int,input().split())) dp = [0 for i in range(m+1)] dp[0] = 1 for i in range(n): j = m while j>=a[i]: dp[j] += dp[j-a[i]] j -= 1 print(dp[m])
# 最优化方案:时间复杂的O(n*sum), 空间复杂度(sum) n, sum = list(map(int,input().split())) A = list(map(int,input().split())) if sum == 0: print(1) else: s = [0 for _ in range(sum)] for i in range(n): if A[i] > sum: continue for j in range(sum-1-A[i], -1, -1): if s[j] > 0: s[j + A[i]] += s[j] s[A[i]-1] += 1 print(s[-1])
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int m = cin.nextInt();
int arr[] = new int[n+1];
//代表你使用前i个数组组成j的最大组合数
long dp[][] = new long[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
arr[i] = cin.nextInt();
}
for (int i = 0; i < m; i++) {
dp[0][i] = 0;
}
//注意,你的dp[0][0]一定要是1,否则会出错
for (int i = 0; i < n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (arr[i] <= j) {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i]];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
System.out.println(dp[n][m]);
}
} // 这应该也算是一个动态规划的问题
// 假设dp[sum]表示部分数组和恰好为sum的方案数
// 因此 dp[sum] = dp[sum-num[1]] + ... dp[sum-num[n]]
// 所以要求dp[sum], 需要求所有的dp[0...sum-1]的方案数
// 实际编程的时候我们不这么计算
// 对于每一个num[i],我们更新所有的dp[0...sum],即,dp[k+num[i]] += dp[k]
// 表达的意思就是假设已经知道dp[k]的方案数,那么num[i]使dp[k+num[i]]增加的方案数肯定是再加上dp[k]的方案数。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
int sum = sc.nextInt();
int[] num = new int[n];
for(int i=0;i<n;i++){
num[i] = sc.nextInt();
}
long[] count = new long[sum+1];
count[0] = 1;
for(int i=0;i<n;i++){
if(num[i]<=sum){
for(int j=sum;j>=0;j--){
if(count[j]>0 && j+num[i]<=sum){
count[j+num[i]] += count[j];
}
}
}
}
System.out.println(count[sum]);
}
}
} import java.util.*;
public class sumA {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (input.hasNext()) {
int n = input.nextInt();
int m = input.nextInt();
int[] arr = new int[n];
for(int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
long[][] dp = new long[n][m + 1];
dp[0][0] = 1;
if(arr[0] <= m) {
dp[0][arr[0]] = 1;
}
for(int i = 1; i < n; i++) {
for(int j = 0; j <= m; j++) {
if(j - arr[i] < 0) {
dp[i][j] += dp[i - 1][j];
}
else {
dp[i][j] += dp[i - 1][j - arr[i]] + dp[i - 1][j];
}
}
}
System.out.println(dp[n - 1][m]);
}
input.close();
}
}
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
long long dp[1005][1005];
int main()
{
using namespace std;
int n, sum;
while (cin >> n >> sum) {
vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
for (int i = 0; i < sum; i++) {
dp[0][i] = 0;
}
for (int i = 0; i < n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
if (nums[i - 1] <= j) {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
cout << dp[n][sum] << endl;
}
return 0;
}
参考优秀回答的二维dp解法
#include <iostream>
using namespace std ;
long long dp[ 1001 ];
int w[ 1001 ];
bool islegal( int j)
{
if (j>= 0 ) return true ;
return false ;
}
int main()
{
int n,sum;
while ( cin >>n>>sum){
for ( int i= 1 ;i<=n;++i)
cin >> w [i];
//dp(j)=dp(j)+dp(j-w[i]) dp(0)=1
for ( int j= 0 ;j<=sum;++j)
if (j== 0 )
dp [j]= 1 ;
else dp [j]= 0 ;
for ( int i= 1 ;i<=n;++i)
{
for ( int j=sum;j>= 0 ;--j)
{
if ( islegal (j- w [i]))
dp [j] = dp [j]+ dp [j- w [i]];
}
}
cout << dp [sum]<< endl ;
}
return 0 ;
}
要用long long才不溢出。。。。
#include <iostream>
using namespace std;
int main()
{
int nCount, nSum;
while (cin >> nCount >> nSum)
{
if (nCount < 1 || nCount > 1000 || nSum < 1 || nSum > 1000)
{
cout << 0 << endl;
continue;
}
int* pNums = new int[nCount];
long long** pDP = new long long*[nCount];
for (int i = 0; i < nCount; ++i)
pDP[i] = new long long[nSum + 1]();
for (int i = 0; i < nCount; ++i)
cin >> pNums[i];
if (nSum >= pNums[0])
++pDP[0][pNums[0]];
for (int i = 1; i < nCount; ++i)
{
for (int j = 1; j <= nSum; ++j)
{
if (j >= pNums[i])
pDP[i][j] = pDP[i - 1][j - pNums[i]] + pDP[i - 1][j];
else
pDP[i][j] = pDP[i - 1][j];
}
if (nSum >= pNums[i])
pDP[i][pNums[i]] = pDP[i - 1][pNums[i]] + 1;
}
cout << pDP[nCount - 1][nSum] << endl;
for (int i = 0; i < nCount; ++i)
delete[] pDP[i];
delete[] pNums;
}
} #include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
int A[1024] = {0};
ll dp[1024] = {1};
int main()
{ int n, m; while (cin >> n >> m)
{
for(int i = 1;i <= n;i++)scanf("%d",&A[i]);
for(int i = 1;i <= n;i++)for(int j = m;j >= A[i];j--)
dp[j] += dp[j - A[i]];
cout << dp[m] << endl; } return 0;
}
动态规划
设m[i][j]是前i个数 和为j的的最大组合方案数
递归公式
1 j=0 //j-a[i]为0时,表示a[i]=j,组合数为1
0 j<0
0 i<1且j!=0
M[i][j]=M[i-1][j]+M[i-1][j-a[i]] //前i-1个 和为j的最大组合数 + 前i-1个 和为 j-a[i]的最大组合数
例如 n=7 sum=8
数据为:5 1 2 4 3 2 3
得到如下矩阵
(i\j) 0 1 2 3 4 5 6 7 8
0 1 0 0 0 0 0 0 0 0
1 1 0 0 0 0 1 0 0 0
2 1 1 0 0 0 1 1 0 0
3 1 1 1 1 0 1 1 1 1
4 1 1 1 1 1 2 2 2 1
5 1 1 1 2 2 3 3 3 3
6 1 1 2 3 3 5 5 6 6
7 1 1 2 4 4 7 8 9 11
例如:m[7][9]=m[6][8]+m[6][8-3]=11
由于当前行只用到了上一行的数值,固可用二维数组存储
public static void main(String[] args)throws Exception{
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
String[] strings=br.readLine().split("\\s");
int n=Integer.parseInt(strings[0]);
int sum=Integer.parseInt(strings[1]);
strings=br.readLine().split("\\s");
int[]data=new int[n];
for(int i=0;i<n;i++){
data[i]=Integer.valueOf(strings[i]);
}
dp(n,sum,data);
}
private static void dp(int n,int sum,int [] data){
long[][] m=new long[2][sum+1];
m[0][0]=1;//j=0
m[1][0]=1;//j=0
for(int i=1;i<=n;i++){
for(int j=1;j<=sum;j++){
if(j>=data[i-1]){
m[1][j]=m[0][j]+m[0][j-data[i-1]];
}else {
m[1][j]=m[0][j];
}
}
// System.out.println(Arrays.toString(m[0]));
for(int k=0;k<=sum;k++){
m[0][k]=m[1][k];
}
}
// System.out.println(Arrays.toString(m[0]));
System.out.println(m[0][sum]);
}
n, sum_wanted = map(int, input().split()) l = [0] + list(map(int, input().split())) res = [[1 if i == 0 else 0 for i in range(sum_wanted + 1)] for j in range(n + 1)] for i in range(1, n + 1): for j in range(1, sum_wanted + 1): if l[i] <= j: res[i][j] = res[i-1][j]+res[i-1][j-l[i]] else: res[i][j] = res[i-1][j] print(res[n][sum_wanted])
常规DP,dp[sum][i] = dp[sum-num[i]][i-1]+dp[sum][i-1] i表示只考虑前i个数。
N,sum_x= map(int,input().split())
list_x = [int(x) for x in input().split()]
len_x = len(list_x)
ans = [[0 for i in range(N+1)] for j in range(sum_x+1)]
for i in range(N+1):
ans[0][i] = 1
for i in range(1,sum_x+1):
for j in range(1,N+1):
if i-list_x[j-1]<0:
ans[i][j] = ans[i][j-1]
else:
ans[i][j] = ans[i][j-1]+ans[i-list_x[j-1]][j-1]
print(ans[-1][-1])
#include <iostream>
using namespace std;
int main()
{ long long dp[1003][1003]; int a[1003],n,sum; while(cin>>n>>sum) { for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<=sum;i++) dp[i][0] = 0; dp[0][0] = 1; for(int i=1;i<=n;i++) { for(int j=0;j<=sum;j++) { dp[i][j] = dp[i-1][j]; if(j >= a[i-1]) dp[i][j] += dp[i-1][j-a[i-1]]; } } cout<<dp[n][sum]<<endl; } return 0;
} import java.util.*;
class Main{
private static int[] twoSum(int n, int[] A,int sum){
Map(Integer, Integer) map = new HashMap<>();
for(int i =0; i<n; i++) {
map.put(A[i],i);
}
for(int i=0;i<n;i++) {
int result = sum - A[i];
if(map.containsKey(result) && map.get(result) !=i) {
return new int[]{A[i],i};
}
}
throw new IllegalArgumentException("不存在这样的方案");
}
} #include <iostream>
#include <vector>
using namespace std;
int main(){ int n,trg; while(cin>>n>>trg){ vector<int> a(n,0); vector<vector<long>> dp(n+1,vector<long> (trg+1,0)); for(int i=0;i<n+1;i++) dp[i][0]=1; for(int& t:a) cin>>t; for(int i=1;i<=n;i++){ for(int j=0;j<=trg;j++){ if(a[i-1]<=j){ dp[i][j]=dp[i-1][j]+dp[i-1][j-a[i-1]]; }else{ dp[i][j]=dp[i-1][j]; } } } cout<<dp[n][trg]<<endl; } return 0;
}