#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
bool canPartition(const vector<int>& nums)
{
int sum = accumulate(nums.begin(),nums.end(),0);
if(sum % 2 != 0)return false;
sum /= 2;
int n = nums.size();
vector<vector<bool>>dp(n + 1,vector<bool>(sum + 1));
for(int i = 0; i <= n; ++i)dp[i][0] = true;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= sum; ++j)
{
dp[i][j] = dp[i-1][j];
if(j - nums[i-1] >= 0) dp[i][j] = dp[i][j] | dp[i-1][j - nums[i-1]];
}
}
return dp[n][sum];
}
int findMaxFrom(const vector<string>& strs,int m, int n)
{
vector<vector<int>>dp(m+1,vector<int>(n+1));
for(auto str:strs)
{
int one = 0, zero = 0;
for(int i = 0; i < str.size(); ++i)
{
if(str[i] == '0')++zero;
else ++one;
}
for(int i = m; i >= zero; --i)
{
for(int j = n; j >= one; --j)
{
dp[i][j] = max(dp[i][j],dp[i-zero][j-one] + 1);
}
}
}
return dp[m][n];
}
int subSet(const vector<int>& nums, int S)
{
int n = nums.size();
vector<vector<int>>dp(n+1,vector<int>(S+1));
dp[0][0] = 1;
for(int i = 1; i <= n; ++i)
{
for(int j = 0; j <= S; ++j)
{
dp[i][j] = dp[i - 1][j];
if(j - nums[i-1] >= 0)dp[i][j]+=dp[i-1][j-nums[i-1]];
}
}
return dp[n][S];
}
int findTargetSumWays(const vector<int>& nums, int S)
{
int sum = accumulate(nums.begin(),nums.end(),0);
if(S > sum || (S + sum) % 2 == 1) return 0;
return subSet(nums,(sum + S) / 2);
}
int lastStoneWeightII(const vector<int>& nums)
{
int sum = accumulate(nums.begin(),nums.end(),0);
int target = sum / 2;
int n = nums.size();
vector<vector<int>>dp(n + 1, vector<int>(target + 1));
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= target; ++j)
{
dp[i][j] = dp[i-1][j];
if(j - nums[i-1] >= 0)
{
dp[i][j] = max(nums[i-1] + dp[i-1][j-nums[i-1]],dp[i-1][j]);
}
}
}
return sum - 2*dp[n][target];
}
int change(const vector<int>& nums,int amount)
{
int n = nums.size();
vector<vector<int>>dp(n + 1, vector<int>(amount + 1));
for(int i = 0; i <= n; ++i)
{
dp[i][0] = 1;
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= amount; ++j)
{
dp[i][j] = dp[i-1][j];
if(j - nums[i-1] >= 0)
{
dp[i][j] += dp[i][j-nums[i-1]];
}
}
}
return dp[n][amount];
}
int coinChange(const vector<int>& coins, int amount)
{
int n = coins.size();
vector<vector<int>>dp(n + 1, vector<int>(amount + 1,INT_MAX));
for(int i = 0; i <= n; ++i)
{
dp[i][0] = 0;
}
for(int i = 1; i <= n; ++i)
{
for(int j = 0; j <= amount; ++j)
{
dp[i][j] = dp[i-1][j];
if(j >= coins[i-1] && dp[i][j-coins[i-1]]!= INT_MAX)
{
dp[i][j] = min(dp[i][j],dp[i][j-coins[i-1]]+1);
}
}
}
return dp[n][amount] == INT_MAX ? -1:dp[n][amount];
}
int change1(const vector<int>& nums, int amount)
{
int n = nums.size();
vector<vector<int>>dp(n + 1,vector<int>(amount+1));
for(int i = 0; i <= n; ++i)
{
dp[i][0] = 1;
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1;j <= amount ; ++j)
{
dp[i][j] = dp[i-1][j];
if(j - nums[i-1] >= 0)dp[i][j]+=dp[i][j-nums[i-1]];
}
}
return dp[n][amount];
}
int canPartition(int n, const vector<int>&prices,const vector<int>&weight,const vector<int>& amount)
{
int n0 = prices.size();
vector<vector<int>>dp(n0+1,vector<int>(n+1));
for(int i = 1; i <= n0; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = dp[i-1][j];
for(int k = 0; k <= amount[i-1]; ++k)
{
if(j - k * prices[i-1] >= 0)
{
dp[i][j] = max(dp[i][j],dp[i-1][j-k*prices[i-1]] + k * weight[i-1]);
}
}
}
}
return dp[n0][n];
}
int main()
{
//LeetCode416
vector<int>v{1,5,11,5};
cout << canPartition(v) << endl;
//LeetCode474
vector<string>strs{"10","0001","111001","1","0"};
int m = 5, n = 3;
cout << findMaxFrom(strs,m,n) << endl;
//LeetCode494
vector<int>nums{1,1,1,1,1};
int s = 3;
cout << findTargetSumWays(nums,s) << endl;
//LeetCode1049
vector<int>numss{2,7,4,1,8,1};
cout << lastStoneWeightII(numss) << endl;
//完全背包问题
//LeetCode322
vector<int>coins{1,2,5};
int amount =11;
cout << coinChange(coins,amount) << endl;
//LeetCode518
vector<int>coin{1,2,5};
amount = 5;
cout << change1(coin,amount) << endl;
//LintCode798
vector<int>prices{3,2};
vector<int>weights{300,160};
vector<int>amounts{1,6};
n = 8;
cout << canPartition(n,prices,weights,amounts) << endl;
return 0;
}