HDU2844 背包求组合数+背包版

Coins

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6843    Accepted Submission(s): 2782


Problem Description
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
 

Source
2009 Multi-University Training Contest 3 - Host by WHU
 

#define DeBUG
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
#define TRUE true
#define FALSE false
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
    return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
#define N 105
int a[100005];
int c[100005];
int dp[100005];
int v;
void ZeroOnePack(int cost , int weight)
{
    for (int i = v; i >= cost; i--)
        if (dp[i] < dp[i - cost] + weight)
            dp[i] = dp[i - cost] + weight;
}
void CompletePack(int cost, int weight)
{
    for (int i = cost; i <= v; i++)
        if (dp[i] < dp[i - cost] + weight)
            dp[i] = dp[i - cost] + weight;
}
void MultiplePack(int cost, int weight, int amount)
{
    if (cost * amount >= v)
        CompletePack(cost, weight);
    else
    {
        for (int k = 1; k < amount;)
        {
            ZeroOnePack(k * cost, k * weight);
            amount -= k;
            k <<= 1;
        }
        ZeroOnePack(amount * cost, amount * weight);
    }
}
int main()
{
#ifdef DeBUGs
    freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
    int n, m;
    while (scanf("%d%d", &n, &m), n || m)
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &c[i]);
        }
        v = m;
        for (int i = 1; i < 100005; i++)
            dp[i] = -INF;
        dp[0] = 0;
        for (int i = 0; i < n; i++)
            MultiplePack(a[i], a[i], c[i]);
        int sum = 0;
        for (int i = 1; i <= m; i++)
        {
            if (dp[i] == i)
                sum++;
        }
        printf("%d\n", sum);
    }

    return 0;
}


题解来自: http://blog.csdn.net/ice_crazy/article/details/8668896
深入理解
/*
分析:
    (好题)背包,二进制的另一种应用(话说LZ貌似初中的时候也想
过这种组合方式额,不过n年后的现在早忘了囧~),既:
        1、2、4可以组合出所有小于8的数;
        1、2、4、8可以组合出所有小于16的数;
        1、2、4、8、16可以组合出所有小于32的数;
        ……



PS一个:
    另外,看网上的代码,几乎都是用dp[i]来表示容量为i这个包包
可以装多少价值,递推公式为dp[j]=MAX(dp[j],dp[j-val[i]]+val[i]),
然后判断dp[i]是否==i,等于了,那么ans++;


    对于这方法,菜鸟卖个萌啰嗦点儿话囧~:
    1、用flag[i]标记i状态是否可达就行了,没必要求容量为i的包包
最大可以装多少val;(所以我的代码里面用的就是(状态now)=(状态now)
||(状态pre))


    2、对于这个方法呀,如果没有理解透的话,是容易粗事儿的囧~~~,
也就有了:
    “为什么我在进行判断的时候,用dp[i]>=i这个式子,
    既if(dp[i]>=i) ans++,
    这种方法写出来的代码也能ac呢?”
这种问题的出现(在一些blog里面看到的)。
    对于这个,只能说这个题是个特例,是不管对于n种物体的哪种物体,
容量和val的比例总是恒定的,而且还是1:1的关系(注意这儿是1:1,而
不是1:2、1:3或者2:3之类的),既dp[j-a]+b中,a=b=val[i],a:b==1:1,
所以才保证了,《如果容量为i的状态可达,那么最后dp[i]必定==i》
(因为a==b么);
    所以么必要用这个状态转移方程了,赶脚砍魔兽的刀用猪身上了囧~~


    就这么多,囧~~~


                                                            2013-03-14
*/
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
const int N=111;
const int M=100111;

int n,m;
int val[N],c[N],flag[M];
int main()
{
	int i,l;
	int k,t,temp;
	while(scanf("%d%d",&n,&m),n||m)
	{
		for(i=0;i=m)
			{
				for(l=val[i];l<=m;l++)
					flag[l]=flag[l] || flag[l-val[i]];
			}
			//0-1背包
			else
			{
				k=1;
				t=c[i];
				while(k=temp;l--)
						flag[l]=flag[l] || flag[l-temp];
					t-=k;
					k<<=1;
				}
				if(t)
				{
					temp=t*val[i];
					for(l=m;l>=temp;l--)
						flag[l]=flag[l] || flag[l-temp];
				}
			}
		}

		int ans=0;
		for(i=1;i<=m;i++)	if(flag[i])	ans++;
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(ACM-DP,ACM-模板)