Margaritas on the River Walk

One of the more popular activities in San Antonio is to enjoy margaritas in the park along the river know as the River Walk. Margaritas may be purchased at many establishments along the River Walk from fancy hotels to Joe’s Taco and Margarita stand. (The problem is not to find out how Joe got a liquor license. That involves Texas politics and thus is much too difficult for an ACM contest problem.) The prices of the margaritas vary depending on the amount and quality of the ingredients and the ambience of the establishment. You have allocated a certain amount of money to sampling different margaritas.

Given the price of a single margarita (including applicable taxes and gratuities) at each of the various establishments and the amount allocated to sampling the margaritas, find out how many different maximal combinations, choosing at most one margarita from each establishment, you can purchase. A valid combination must have a total price no more than the allocated amount and the unused amount (allocated amount – total price) must be less than the price of any establishment that was not selected. (Otherwise you could add that establishment to the combination.)

For example, suppose you have $25 to spend and the prices (whole dollar amounts) are:

Vendor A B C D H J
Price 8 9 8 7 16 5

Then possible combinations (with their prices) are:

ABC(25), ABD(24), ABJ(22), ACD(23), ACJ(21), ADJ( 20), AH(24), BCD(24), BCJ(22), BDJ(21), BH(25), CDJ(20), CH(24), DH(23) and HJ(21).

Thus the total number of combinations is 15.

Input

The input begins with a line containing an integer value specifying the number of datasets that follow, N (1 ≤ N ≤ 1000). Each dataset starts with a line containing two integer values V and D representing the number of vendors (1 ≤ V ≤ 30) and the dollar amount to spend (1 ≤ D ≤ 1000) respectively. The two values will be separated by one or more spaces. The remainder of each dataset consists of one or more lines, each containing one or more integer values representing the cost of a margarita for each vendor. There will be a total of V cost values specified. The cost of a margarita is always at least one (1). Input values will be chosen so the result will fit in a 32 bit unsigned integer.

Output

For each problem instance, the output will be a single line containing the dataset number, followed by a single space and then the number of combinations for that problem instance.

Sample Input
2
6 25
8 9 8 7 16 5
30 250
1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
Sample Output
1 15
2 16509438
Hint

Note: Some solution methods for this problem may be exponential in the number of vendors. For these methods, the time limit may be exceeded on problem instances with a large number of vendors such as the second example below.

题意:多组输入,每组给定物品数(<=30)和背包容量(<=1000)以及接下来每个物品的体积,问有多少种方案,使得装入一些物品后,无法装入剩下的任意一个物品。

可以转化成0-1背包来做,首先按体积从小到大排序,枚举“剩下的物品”中体积最小的。剩下的物品中体积最小的为i时,前i-1个物品是必然被装入背包的,然后对第i+1到第n个物品做0-1背包问题,转移方程f[i][j]表示前i个物品装入容量为j的背包的方案数,f[i][j] = f[i-1][j] + f[i-1][j-v[j]]。那么便有了这个O(T*n*n*C)的做法。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1000 + 10;
typedef long long ll;
ll d[maxn];
ll v[maxn];
int main()
{
    int t,n,m;
    cin>>t;
    for(int k = 1; k <= t; k++)
    {
        cin>>n>>m;
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld",&v[i]);
        }
        sort(v+1, v+1+n);
        if(v[1] > m)
        {
            printf("%d 0\n",k);
            continue;
        }
        ll ans = 0,sum = 0;
        for(int i = 1; i <= n; i++)
        {
            if(sum > m) break;
            memset(d,0,sizeof d);
            d[sum] = 1;
            for(int j = i+1; j <= n; j++)
            {
                for(int l = m; l >= sum+v[j]; l--)
                    d[l] += d[l-v[j]];
            }
            for(int j = m-v[i]+1; j <= m; j++)
                if(j >= sum)
                    ans += d[j];
            sum += v[i];
        }
        printf("%d %lld\n",k,ans);
    }
    return 0;
}
View Code

优化:逆序枚举,按体积从大到小,减少了重复对第i+1到第n个物品做0-1背包的计算

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1000 + 10;
typedef long long ll;
ll d[maxn];
ll v[maxn];
int main()
{
    int t,n,m;
    cin>>t;
    for(int k = 1; k <= t; k++)
    {
        cin>>n>>m;
        ll sum = 0;
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld",&v[i]);
            sum += v[i];
        }
        sort(v+1, v+1+n);
        if(v[1] > m)
        {
            printf("%d 0\n",k);
            continue;
        }
        ll ans = 0;
        memset(d,0,sizeof d);
        d[0] = 1;
        for(int i = n; i >= 1; i--)
        {
            sum -= v[i];
            int k = max((ll)0,m-sum-v[i]+1);
            for(int j = k; j <= m-sum; j++)
                ans += d[j];
            for(int j = m; j >= v[i]; j--)
                d[j] += d[j-v[i]];
        }
        printf("%d %lld\n",k,ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lwsh123k/p/9487475.html