HDU 2844&& POJ 1742

来源:互联网 发布:淘宝买电脑可靠吗 编辑:程序博客网 时间:2024/05/17 21:56

Whuacmers use coins.They have coins of value A1,A2,A3…An Silverland dollar. One day Hibix opened purse 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

多重背包问题:
题目求的是m以内的组合数

算出具体值(HDU能过但是POJ会超时):

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 100050#define INF 0x3f3f3f3fint dp[maxn], a[maxn], c[maxn];int main(){    int n, m, i, j, k, ans;    while(~scanf("%d %d",&n, &m) && (n!=0||m!=0)){        memset(dp, -INF,sizeof(dp));        memset(a, 0,sizeof(a));        memset(c, 0,sizeof(c));        for(i = 1; i <= n; ++i){            scanf("%d", &a[i]);        }        for(j = 1; j <= n; ++j){            scanf("%d", &c[j]);        }        dp[0] = 1;        for(i = 1; i <= n; ++i){            if(a[i] * c[i] >= m){                for(j = a[i]; j <= m; ++j){                    dp[j] = max(dp[j], dp[j-a[i]] + a[i]);                }            }            else{                for(k = 1; k <= c[i]; k *= 2){                    for(j = m; j >= a[i] * k; --j){                        dp[j] = max(dp[j], dp[j-a[i]*k]+a[i]*k);                    }                    c[i] -= k;                }                if(c[i] > 0){                    for(j = m; j >= a[i] * c[i]; --j){                        dp[j] = max(dp[j], dp[j-a[i]*c[i]]+a[i]*c[i]);                    }                }            }        }        ans = 0;        for(j = 1; j <= m; ++j){            if(dp[j] >= 0){                ans++;            }        }        printf("%d\n", ans);    }}  

因为题目要求的只是m以内的组合数,dp[i][j]记录前i种硬币价值为j是否有组合,num[i][j]记录硬币i在价值为j时的数量,优化空间用滚动数组。

TLE问题解决!(这种做法一般只适用于限制条件与所求值是对应的,一般的多重背包问题重量是限制条件,要求的是最大价值,这一题限制条件是金钱,要求的也是金钱。转移状态方程长得像这个样子的→_→dp[j] = max(dp[j], dp[j-a[i]] + a[i]);)

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;#define maxn 100050#define INF 0x3f3f3f3fint dp[maxn], num[maxn], a[maxn], c[maxn];int main(){    int n, m, i, j, k, ans;    while(~scanf("%d %d",&n, &m) && (n!=0||m!=0)){        memset(dp, 0,sizeof(dp));        for(i = 1; i <= n; ++i){            scanf("%d", &a[i]);        }        for(j = 1; j <= n; ++j){            scanf("%d", &c[j]);        }        dp[0] = 1;        ans = 0;        for(i = 1; i <= n; ++i){            memset(num, 0, sizeof(num));            for(j = a[i]; j <= m; ++j){                if(!dp[j] && c[i] > num[j - a[i]]&&dp[j-a[i]]){                    num[j] = num[j-a[i]] + 1;                    dp[j] = 1;                    ++ans;                }            }        }        printf("%d\n", ans);    }}