输入 N (N 是正整数, N <= 200)
输入 N 个价格p(正整数, p <= 10000)用单空格分割
输入金额 M(M是正整数,M <= 100000 )
能组合出来输出 1
否则输出 0
6 99 199 1999 10000 39 1499 10238
1
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 strN; while((strN = br.readLine()) != null) { int n = Integer.parseInt(strN); String[] strPrice = br.readLine().trim().split(" "); int[] price = new int[n]; for(int i = 0; i < n; i++) price[i] = Integer.parseInt(strPrice[i]); int m = Integer.parseInt(br.readLine().trim()); System.out.println(solve(price, m)); } } private static int solve(int[] price, int m) { int n = price.length; // dp[i][j]表示用0~i的物品能否凑到价值为j的礼包 boolean[][] dp = new boolean[n][m + 1]; // 用0~n-1的物品肯定都能凑成价值为0的礼包 for (int i = 0; i < n; i++) dp[i][0] = true; // 仅用物品0,只能构成价值为price[0]的礼包,所以只有相等的时候为true for (int j = 0; j <= m; j++) if(price[0] == j) dp[0][j] = true; // 动态规划 for(int i = 1; i < n; i++){ for (int j = 1; j <= m; j++) { if(j < price[i]) // 无法选择物品i,选了就会超过价值j dp[i][j] = dp[i - 1][j]; else{ /*可以选择物品i,此时的状态取决于使用0~i-1的物品是否构成了 价值为j的礼包,如果构成了,从状态dp[i-1][j]直接转移过来, 本次不选择i物品。否则从dp[i][j-price[i]]转移过来,本次 选择物品i */ dp[i][j] = dp[i - 1][j] || dp[i - 1][j - price[i]]; } } } return dp[n - 1][m] ? 1: 0; } }
#include <bits/stdc++.h> using namespace std; int main(){ int n, m, s=1; cin>>n; int a[n]; for(int i=0;i<n;i++) cin>>a[i]; cin>>m; bool dp[m+1]; memset(dp, false, sizeof(dp)); dp[0] = true; for(int i=0;i<n;i++) for(int j=m;j>=a[i];j--) dp[j] = dp[j] || dp[j-a[i]]; cout<<dp[m]<<endl; return 0; }
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } int target = sc.nextInt(); boolean[][] dp = new boolean[n][target + 1]; dp[0][0] = true; for (int i = 1; i <= target; i++) { if(arr[0] == i) { dp[0][i] = true; } } for (int i = 1; i < n; i++) { for (int j = 0; j <= target; j++) { dp[i][j] = dp[i - 1][j]; if(j >= arr[i]) { dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i]]; } } } System.out.println(dp[n - 1][target] ? 1 : 0); } }
简单暴力递归,超时
//通过65% bool count(int sum,int currend_index) { if (sum == total) return true; else if (sum > total||currend_index>=n) return false; else return count(sum, currend_index + 1) || count(sum + m_array[currend_index], currend_index + 1); }
优化为背包遍历,就可以了
#include <iostream> #include <vector> using namespace std; vector m_array; int n, total; int main() { int temp; cin >> n; for (unsigned int j = 0;j < n;++j) { cin >> temp; m_array.push_back(temp); } cin >> total; //cout << count(0,0); vector result(total+1,0); result[0] = 1; for (unsigned int i = 0;i < n;++i) { for (unsigned int j = total;j >= m_array[i];--j) { result[j] = result[j] || result[j - m_array[i]]; } } cout << result[total]; system("pause"); return 0; }
/* 0-1背包变式,通过率低的原因应该是除了C/C++其他都超时 */ #include <bits/stdc++.h> using namespace std; int main(void) { int n; cin >> n; int p[n], i, j, m; for(i = 0; i < n; i++) cin >> p[i]; cin >> m; bool ans[m + 1]; memset(ans, 0, sizeof(ans)); ans[0] = true; for(i = 0; i < n; i++) { for(j = m; j >= p[i]; j--) { ans[j] = ans[j] || ans[j - p[i]]; } } cout << ans[m] << endl; return 0; }
典型的0-1背包问题,状态转移方程为f[i][j]=f[i-1][j] || f[i-1][j-v[i]]
,优化为一维,从右往左更新一维数组,初始化f[0]=1,因为m为0时不取任何物品就成立。
int solve(){
vector<bool> f(m + 1);
f[0] = 1;
for(int i = 0; i < n; i++){
for(int j = m; j >= v[i]; j--){
f[j] = f[j] || f[j - v[i]];
}
}
return f[m];
}
这里数组v为输入的元素,n为元素个数,m为金额,处理输入和main就不放上来了。更详细的分析请参考LeetCode 416。
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> using namespace std; int main() { int n, m; scanf("%d", &n); vector<int> A(n+1); for (int i = 1; i <= n; ++i) scanf("%d", &A[i]); scanf("%d", &m); vector<bool> dp(m+1, false); dp[0] = true; int preSum = 0; for (int i = 1; i <= n; ++i) { preSum += A[i]; for (int s = min(preSum, m); s >= 0; --s) if (s >= A[i]) dp[s] = dp[s] | dp[s-A[i]]; } printf("%d\n", dp[m]?1:0); return 0; }
//回溯法 超时了,通过率只有70% import java.util.*; public class Main{ static int mark = 0; public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] arr = new int[n]; for(int i = 0;i<arr.length;i++){ arr[i]=sc.nextInt(); } int price = sc.nextInt(); helper(arr,new int[n],price); System.out.println(mark); } public static void helper(int[] arr,int[] flag,int price){ if(mark==1||price<0){ return; } if(price==0){ mark = 1; return; } for(int i = 0;i<arr.length;i++){ if(flag[i]==1){ continue; } flag[i]=1; price-=arr[i]; helper(arr,flag,price); price+=arr[i]; flag[i]=0; } } }
#include <bits/stdc++.h> using namespace std; int p[205]; bitset<100001> dp(0); int main() { int n, m; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", p + i); scanf("%d", &m); dp[0] = 1; for (int i = 0; i < n; i++) dp |= dp << p[i]; cout << dp[m] << endl; return 0; }
package 小米; import java.util.Scanner; public class 小米大礼包 { public static void main(String[] args) { Scanner in = new Scanner(System.in); while(in.hasNext()){ int N = in.nextInt(); int save[] = new int[N]; for(int i = 0;i < N;i++){ save[i] = in.nextInt(); } int sum = in.nextInt(); int num[] = new int[sum+1]; num[0] = 1; for(int i = 0;i < save.length;i++){ for(int j = sum;j>=1;j--){ if(j>=save[i]){ if(num[j]==1){ continue; }else{ if(num[j-save[i]]==1){ num[j] = 1; } } } } } System.out.println(num[sum]); } } }
01背包问题。
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] v = new int[n]; for(int i = 0; i < n; i ++) { v[i] = sc.nextInt(); } int m = sc.nextInt(); boolean[] dp = new boolean[m + 1]; dp[0] = true; for(int i = 0; i < n; i ++) { for(int j = m; j >= v[i]; j --) { dp[j] = dp[j] || dp[j - v[i]]; } } System.out.println(dp[m] ? 1 : 0); } }
#include <bits/stdc++.h> using namespace std; const int maxn = 200 + 10; const int maxm = 1e5 + 10; bool dp[maxm]; int arr[maxn]; int n, m; int ans; int main(int argv, char* argc[]) { scanf("%d", &n); for (int i = 0; i < n; ++i) scanf("%d", &arr[i]); scanf("%d", &m); dp[0] = true; for (int i = 1; i <= n; ++i) { for (int j = m; j > 0; --j) { if (arr[i - 1] <= j) dp[j] |= dp[j - arr[i - 1]]; } } cout << dp[m] << endl; return 0; }
import java.util.*; public clas***ain{ public static void main(String[] args){ Scanner sc=new Scanner(System.in); while(sc.hasNext()){ int n=sc.nextInt(); int[] arr=new int[n]; for(int i=0;i<n;i++){ arr[i]=sc.nextInt(); } int sum=sc.nextInt(); if(getResult(arr,sum)){ System.out.println(1); }else{ System.out.println(0); } } } //dp[i][j]表示arr的前i个数能否得到和j,每一个数都可以取或者不取 public static boolean getResult(int[] arr,int sum){ int n=arr.length; boolean[][] dp=new boolean[n+1][sum+1]; dp[0][0]=true; for(int i=1;i<=n;i++){ dp[i][0]=true; } for(int j=1;j<=sum;j++){ dp[0][j]=false; } for(int i=1;i<=n;i++){ for(int j=1;j<=sum;j++){ dp[i][j]=dp[i-1][j]; if(j>=arr[i-1]){ dp[i][j]=dp[i][j]||dp[i-1][j-arr[i-1]]; } } } return dp[n][sum]; } }