POJ 1742(DP之多重部分和问题)

Coins
Time Limit: 3000MS   Memory Limit: 30000K
Total Submissions: 41899   Accepted: 14191

Description

People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar.One day Tony opened his money-box and found there were some coins.He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch. 
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins. 

Input

The input contains several test cases. The first line of each test case contains two integers n(1<=n<=100),m(m<=100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1<=Ai<=100000,1<=Ci<=1000). The last test case is followed by two zeros.

Output

For each test case output the answer on a single line.

Sample Input

3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0

Sample Output

8
4

题目大意是让你从所给的面值中选若干硬币(不超过对应的Ci个),所选的硬币价值和不能超过M,求多少种情况。

对于dp问题首先建立一个dp方程

 

dp[i+1][j]表示用前i种数加和得到j时第i种数最多还剩下多少。

具体参考动态规划之多重部分和问题

下面直接上代码

#include <iostream>
#include <algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int num[105];
int price[105];
int dp[105][1000005];
int main()
{
    int n,m;
        while(cin>>n>>m&&(n||m))
    {
        memset(price,0,sizeof(price));
        memset(num,0,sizeof(num));
        memset(dp,-1,sizeof(dp));
        for(int i=0;i<n;i++)
            cin>>price[i];
        for(int i=0;i<n;i++)
            cin>>num[i];
        dp[0][0]=0;//这步是为了说明前0种数构成和为零的情况是0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=m;j++)//一般的dp数组都是要处理一下下标是0的情况的,由前面往后推嘛
            {
                if(dp[i-1][j]>=0)
                    dp[i][j]=num[i-1];
                else if(j<price[i-1]||dp[i][j-price[i-1]]<=0)
                    dp[i][j]=-1;
                else
                    dp[i][j]=dp[i][j-price[i-1]]-1;
            }
        }
        int sum=0;
        for(int i=1;i<=m;i++){
                if(dp[n][i]>=0)//这里只需要保证最后一次,就是前n种数的种类情况能够凑出和为j的可行性(0不用考虑),如果能够凑出和为j,就是
                sum++;        //就是说明dp数组中的[n][j]>=0,即所剩下的元素个数要  >=0 ;
            }
            //sum--;
        cout<<sum<<endl;
//         for(int i=0;i<=n;i++)
//    {
//        for(int j=0;j<=m;j++)
//            cout<<dp[i][j]<<" ";
//        cout<<endl;
//    }
    }
    return 0;
}

注意啊:上面的方法是会MLE(内存超限)

下面进行处理,思路差不多

#include <iostream>
#include <algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int num[105];
int price[105];
int dp[100005];
int main()
{
    int n,m;
        while(cin>>n>>m&&(n||m))
    {
        memset(price,0,sizeof(price));
        memset(num,0,sizeof(num));
        memset(dp,-1,sizeof(dp));
        for(int i=0;i<n;i++)
            cin>>price[i];
        for(int i=0;i<n;i++)
            cin>>num[i];
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=m;j++)
            {
                if(dp[j]>=0)
                    dp[j]=num[i-1];//前i种元素能够凑出j的最多还剩多少,不够就看此时的面值能不能刚好凑出j
                else if(j<price[i-1]||dp[j-price[i-1]]<=0)
                    dp[j]=-1;
                else
                    dp[j]=dp[j-price[i-1]]-1;
            }
        }
        int sum=0;
        for(int i=1;i<=m;i++){
                if(dp[i]>=0)
                sum++;
            }

        cout<<sum<<endl;

    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/c___c18/article/details/80711222
今日推荐