输出包括两行,第一行包括两个整数n和aim。第二行包含n个整数,表示arr数组。
输出一个整数,表示换钱的方法数对取模后的答案。
4 15 5 10 25 1
6
5*3=15
10*1+5*1=15
10*1+1*5=15
1*10+5*1=15
5*2+1*5=15
1*15=15
5 1000 2 3 5 7 10
20932712
时间复杂度,空间复杂度。
#include <bits/stdc++.h> using namespace std; const int MOD = 1e9+7; int main(){ int n, m; cin>>n>>m; long a[n], dp[m+1]; memset(dp, 0, sizeof(dp)); for(int i=0;i<n;i++) cin>>a[i]; dp[0] = 1; for(int i=0;i<n;i++) for(int j=a[i];j<=m;j++) dp[j] = (dp[j]+dp[j-a[i]])%MOD; cout<<dp[m]%MOD<<endl; return 0; }
#include<bits/stdc++.h> using namespace std; int main() { int n,aim,M=1e9+7; cin>>n>>aim; if(n==0||aim<0) { cout<<-1<<endl; return 0; } vector<int>arr(n); for(int i=0;i<n;i++) cin>>arr[i]; vector<int>dp(aim+1,0); dp[0]=1; // 组成0元的个数 for(int i=0;i<n;++i) for(int j=arr[i];j<=aim;++j) dp[j]=(dp[j]+dp[j-arr[i]])%M; cout<<dp[aim]<<endl; return 0; }
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] params = br.readLine().trim().split(" "); int n = Integer.parseInt(params[0]); int target = Integer.parseInt(params[1]); final int MOD = 1000000007; String[] strArr = br.readLine().trim().split(" "); int[] coins = new int[n]; for(int i = 0; i < n; i++) coins[i] = Integer.parseInt(strArr[i]); int[] dp = new int[target + 1]; dp[0] = 1; for(int coin: coins){ for(int i = coin; i <= target; i++) dp[i] = (dp[i] + dp[i - coin]) % MOD; } System.out.println(dp[target]); } }如果动态规划的状态转移方程一开始很难想到,那我们可以通过记忆化搜索的递归版解法改编过来,只是将递归的调用,改成从DP表中取值就行:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { static final int MOD = 1000000007; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] params = br.readLine().trim().split(" "); int n = Integer.parseInt(params[0]); int target = Integer.parseInt(params[1]); String[] strArr = br.readLine().trim().split(" "); int[] coins = new int[n]; for(int i = 0; i < n; i++) coins[i] = Integer.parseInt(strArr[i]); int[][] memory = new int[n + 1][target + 1]; // 从左边的硬币开始使用,往右尝试 System.out.println(recurrent(coins, 0, target, memory)); } private static int recurrent(int[] coins, int index, int rest, int[][] memory) { if(index == coins.length){ return rest == 0? 1: 0; }else{ // 缓存命中直接返回 if(memory[index][rest] > 0) return memory[index][rest]; // 否则递归计算 memory[index][rest] = (recurrent(coins, index + 1, rest, memory) + (rest - coins[index] >= 0? recurrent(coins, index, rest - coins[index], memory): 0)) % MOD; return memory[index][rest]; } } }
import scala.io.StdIn object Main { def main(args: Array[String]): Unit = { val in = StdIn var params = in.readLine().split(" ") val n = params(0).toInt val aim = params(1).toInt params = in.readLine().split(" ") val coins = Array.ofDim[Int](n) for(i <- coins.indices) coins(i) = params(i).toInt val dp = Array.ofDim[Long](n + 1, aim + 1) dp(n)(0) = 1 for(index <- n - 1 to 0 by -1; rest <- 0 to aim){ dp(index)(rest) = dp(index + 1)(rest) if(rest - coins(index) >= 0) dp(index)(rest) = (dp(index)(rest) + dp(index)(rest - coins(index))) % 1000000007 } println(dp(0)(aim)) } }
#include "bits/stdc++.h" using namespace std; int main() { const int M=1e9+7; int N;cin>>N; int aim;cin>>aim; vector<int> money(N); for(int i=0;i<N;i++)cin>>money[i]; //sort(money.begin(),money.end()); vector<int> dp(aim+1,0); //cout<<1; dp[0]=1; for(int j=0;j<N;j++) { for(int i=money[j];i<=aim;i++) { //if(i>money[j]){ dp[i]=(dp[i]+dp[i-money[j]])%M; //else if(i==money[j])dp[i]++; } //cout<<dp[i]<<' '; } cout<<dp[aim]; return 0; }
import java.util.*; public class Main { public static int mod = (int) 1e9+7; public static void main(String[] args) { Scanner sc = new Scanner (System.in); int n = sc.nextInt(); int aim = sc.nextInt(); int [] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } System.out.println(numDP(arr, aim)); } public static int numDP (int[] arr, int aim) { if (arr == null || arr.length < 1 || aim < 0) return 0; int n = arr.length; long[][] dp = new long[n+1][aim+1]; dp[n][0] = 1; for (int i = n-1; i >= 0; i--) { for (int j = 0; j<=aim; j++) { dp[i][j] = (dp[i+1][j]) % mod; if (j - arr[i] >= 0) dp[i][j]+= (dp[i][j-arr[i]]) % mod; dp[i][j] = dp[i][j] % mod; } } return (int)dp[0][aim]; } }
#include <iostream> #include <vector> using namespace std; const int mod = 1e9 + 7; int process(int i, int rest, const vector<int>& v, vector<vector<int>>& dp){ if(rest < 0) return 0; if(dp[i][rest] != -1) return dp[i][rest]; if(rest == 0){ dp[i][rest] = 1; return dp[i][rest]; } //rest > 0 if(i == v.size()){ dp[i][rest] = 0; return dp[i][rest]; } //rest > 0 ; i < v.size() long ans = 0; for(int num = 0; num * v[i] <= rest; num++){ ans += (process(i + 1, rest - num * v[i], v, dp) % mod); } dp[i][rest] = ans % mod; return dp[i][rest]; } int way(int aim, const vector<int>& v){ vector<vector<int>> dp(v.size() + 1, vector<int>(aim + 1, -1)); return process(0, aim, v, dp); } int main(void){ int n, aim; cin >> n >> aim; vector<int> v(n); for(int i = 0; i < n; i++){ cin >> v[i]; } cout << way(aim, v) << endl; return 0; }
#include <iostream> #include <vector> using namespace std; const int mod = 1e9 + 7; int way3(int aim, const vector<int>& v){ int N = v.size(); vector<vector<int>> dp(N + 1, vector<int>(aim + 1)); for(int i = 0; i <= N; i++) dp[i][0] = 1; for(int i = N - 1; i >= 0; i--){ for(int rest = 1; rest <= aim; rest++){ dp[i][rest] = dp[i + 1][rest]; if(rest - v[i] >= 0) dp[i][rest] = ((long)dp[i][rest] + (long)dp[i][rest - v[i]]) % mod; } } return dp[0][aim]; } int main(void){ int n, aim; cin >> n >> aim; vector<int> v(n); for(int i = 0; i < n; i++){ cin >> v[i]; } cout << way3(aim, v) << endl; return 0; }斜率优化的动态规划版本
#include <cstdio> #define mod 1000000007; long long n, aim, arr[1010], dp[20010] = {1}, i, j; int main(){ scanf("%lld %lld", &n, &aim); for(i = 0; i < n; i++) scanf("%lld", &arr[i]); for(j = n-1; j >= 0; j--) for(i = arr[j]; i <= aim; i++) dp[i] = (dp[i] + dp[i - arr[j]]) % mod; printf("%lld", dp[aim]); }
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int aim = sc.nextInt(); int[] arr = new int[n+1]; for(int i=1;i<=n;i++){ arr[i]=sc.nextInt(); } long[] dp = new long[aim+1]; dp[0]=1; for(int i=1;i<=n;i++){ for(int j=arr[i];j<=aim;j++){ dp[j] = (dp[j]+dp[j-arr[i]])%1000000007; } } System.out.println(dp[aim]); } }
完全背包求方案数
dp[j]:考虑前 n 种货币,组成面值 j 的方案数
import java.util.Scanner; public class Main { static int N = 20010; static int mod = (int) (1e9 + 7); static int n, aim; static int[] dp = new int[N]; public static void main(String[] args) { Scanner sc = new Scanner(System.in); n = sc.nextInt(); aim = sc.nextInt(); dp[0] = 1; for (int i = 0; i < n; i++) { int v = sc.nextInt(); for (int j = v; j <= aim; j++) { dp[j] = (dp[j] + dp[j - v]) % mod; } } System.out.println(dp[aim]); } }
import java.util.*; public class Main { public static final int mod = 1_000_000_007; public static int process(int[] arr, int n, int aim) { if (arr == null || arr.length == 0) return 0; int[] dp = new int[aim + 1]; for (int i = 0; i < n; i++) { dp[0] = 1; for (int target = 0; target <= aim; target++) { if (target - arr[i] >= 0) { if (i == 0) dp[target] = dp[target - arr[i]]; else dp[target] = (dp[target] + dp[target - arr[i]]) % mod; } } } return dp[aim]; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int aim = sc.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } System.out.println(process(arr, n, aim)); } }
#include<iostream> (720)#include<vector> using namespace std; int change(int amount, vector<int> &coins) { int mod = 1e9 + 7; if (amount == 0) return 1; if (coins.size() == 0) return 0; vector<int> dp(amount + 1, 0); dp[0] = 1; for (int i = 0; i < coins.size(); i++) { for (int j = coins[i]; j <= amount; j++) { dp[j] += dp[j - coins[i]]; dp[j] %= mod; } } return dp[amount]; } int main () { int n, aim; cin >> n >> aim; int *arr = new int[n]; vector<int> tmp; for (int i = 0; i < n; i++) { cin >> arr[i]; tmp.push_back(arr[i]); } cout << change(aim, tmp) << endl; return 0; }clear code to easy understand
//完全背包问题变种,二维数组会内存超限,所以用滚动数组实现1维的dp #include<cstdio> #include<iostream> #include<cstdlib> using namespace std; int main(){ int n,aim; cin>>n>>aim; long long arr[n]; for(int i=0;i<n;i++){ cin>>arr[i]; } long long dp[30000]; dp[0] = 1; for(int i=0;i<n;i++){ for(int j=1;j<=aim;j++){ if(j>=arr[i]) dp[j] = (dp[j-arr[i]]+dp[j])%1000000007; } } cout<<dp[aim]%1000000007; return 0; }
# -*- coding: utf-8 -*- # !/usr/bin/python3 import sys while True: s = sys.stdin.readline().strip() if s == '': break else: n, t = s.split() n = int(n) t = int(t) arr = list(map(int, input().split())) arr = sorted(arr) res = [0 for i in range(t + 1)] res[0] = 1 for i in arr: if i > t: break for j in range(i, t + 1): res[j] = res[j] + res[j - i] # 输出一个整数,表示换钱的方法数对10 ^ 9 +7取模后的答案。 print(res[t] % (pow(10, 9) + 7)) ''' 解题思路: 动态规划核心状态转移方程式 f(n) = f(n - arr[0]) + f(n - arr[1]) + f(n - arr[2]) + ... + f(n - arr[i]) i为arr的长度 对于组成n的情况,每个arr[i]元与f(n - arr[i])都可以组成一个n,所以把每种f(n - arr[i])的种类数 相加,就找出了arr能组成n的种类。递归计算会重复计算f(n - arr[i]),运用斐波那契数列类似的方法, 从0开始遍历,重复利用计算过的f(n - arr[i])。结果数值会过大,题目要求输出对10 ^ 9 +7取模后的答案。 '''
// dp #include <bits/stdc++.h> using namespace std; int main() { // 输出并创建数组 int n,aim; cin>>n>>aim; vector<int>arr(n,0); for(int i=0;i<n;++i){ cin>>arr[i]; } int M=1e9+7; // 定义模数 vector<int>dp(aim+1,0); dp[0]=1; // 组成0元的个数 for(int i=0;i<n;++i){ for(int j=arr[i];j<=aim;++j){ dp[j]=(dp[j]+dp[j-arr[i]])%M; } } cout<<dp[aim]<<endl; return 0; }
import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int aim = sc.nextInt(); int[] nums = new int[n]; for(int i=0;i<n;i++){ nums[i] = sc.nextInt(); } //一维dp int[] dp = new int[aim+1]; dp[0] = 1; for(int i=0;i<n;i++){ for(int j=nums[i];j<=aim;j++){ dp[j] = (dp[j] + dp[j-nums[i]]) % 1000000007; } } System.out.println(dp[aim]); /** 二维dp int[][] dp = new int[n+1][aim+1]; dp[0][0] = 1; for(int i=1;i<=n;i++){ dp[i][0] = 1; for(int j=1;j<=aim;j++){ //每个金额的货币,有取和不取两种选择。 dp[i][j] = (dp[i-1][j] + (j>=nums[i-1]?dp[i][j-nums[i-1]]:0)) % 1000000007; } } System.out.println(dp[n][aim]); */ } }
def count_way(arr, n): # 第0位是辅助位,代表面额数刚好等于金钱数的一种情况 dp = [1] + [0]*n # 不用任意面额的组合数 for num in arr: for j in range(num, n+1): dp[j] += dp[j-num] return dp[-1]%(1000000007) _,n = list(map(int, input().split(' '))) arr = list(map(int, input().split(' '))) print(count_way(arr, n))同样是动态规划,python比其他语言慢了好多