砝码问题

设有1g,2g,3g,5g,10g,20g的砝码各若干枚(其总重<=1000g),要求:

输入:a1,a2,a3,a4,a5,a6(表示1g的砝码有a1,2g的砝码有a2.

……20g的砝码有a6)

       输出:Total=n(n表示这些砝码能称出的不同重量的个数,但不包括

一个砝码也不用的情况。

       Sample Input:

             1 1 0 0 0 0

       Sample Output:

             Total=3     (表示可以称出1g,2g,3g三种不同的重量)

解答:我们用Q[i,j]表示用前i个砝码是否能称量重量为j的物品。Q[i,j]=1表示可以称量,Q[i,j]=0表示不能称量。那么状态转移方程为:(我们用数组w[i]表示每个砝码的重量,用a[i]表示每个砝码有多少个,1=<i<=6 )

     Q[i,j]=Q[i-1,j] or Q[i-1,j-w[i]] or Q[i-1,j-2*w[i]]....or Q[i-1,j-k*w[i]]

其中k=min{a[i],|j/w[i]|}(向下取整),边界条件为:Q[0,0]=1,最终要求的值为Q[6,j]=1的那些j, 1=<j<=N (N=SUM(w[i]*a[i])),设定砝码总的重量为N,代码如下:

//背包问题的扩展:砝码问题a保存每种砝码的个数,w保存每种砝码的重量,n是砝码的种类数。

void WeightPro(int *a,int *w,int n){

//首先计算总的重量

int N=0;

for(int i=1;i<=n;i++){

N+=a[i]*w[i];

}

//首先定义一个二维bool数组

bool **Q=new bool*[n+1];

for(int i=0;i<=n;i++){

Q[i]=new bool[N+1]();

}

Q[0][0]=true;

for(int i=1;i<=n;i++){

for(int j=0;j<=N;j++){

int q=j/w[i];

int k=q>a[i]?a[i]:q;

for(int p=0;p<=k;p++){

Q[i][j]|=Q[i-1][j-p*w[i]];

}

}

}

int count=0;

cout<<"总重量为:"<<N<<endl;

for(int i=N;i>0;i--){

if(Q[n][i]){

count++;

cout<<"能够称量重量为:"<<i<<"的物品"<<endl;

}

}

cout<<"不同重量个数为:"<<count<<endl;

}

时间代价为O(N*n),空间为O(n*N),这个题目的空间可以优化O(N),因为我们看到状态转移方程中的Q[i,j]仅仅依赖于Q[i-1,..]的状态,但要求j是递减,而且这一点是必须的,因为这样才能保证Q[i,j]当前的状态依赖于i-1的状态,其状态转移方程为:(j from N to 0);

Q[j]=Q[j] or Q[j-w[i]] or Q[j-2*w[i]]....or Q[j-k*w[i]]


变种:这个题目可以进一步变形为对于某个重量为m的物品,该组砝码能够称量m所用的最少的砝码为多少,是哪些砝码?这个问题就和之前的找钱问题是一样的了。只不过这里对砝码的数量有限制,只需要在状态转移方程上稍作改动即可。

令Q[i][j]为有前i个砝码称量出m的最少砝码个数,那么状态转移方程为:

Q[i,j]=min{Q[i-1,j-k*w[i]]+k| 0=<k<=min{j/w[i], a[i]} }

(如果砝码个数是无限个)上面这个转移方程实现起来不是>O(N*n)还可以把状态转移方程优化为:
Q[i,j]=min{Q[i-1,j],Q[i,j-w[i]] +1 }
(如果砝码个数是无限个)上面这个转移方程实现起来就是O(N*n)的了,但空间是O(N*n)的,空间可以进一步优化为O(N)的,对应的状态转移方程为:
Q[j]=min{Q[j],Q[j-w[i]]+1}
但如果要记录用了哪些砝码,必须要开一个O(N*n)的空间来trace.

还可以变种为对于一个给定的重量为m的物品,求出所有能够称量出m的砝码组合数,每一个组合分别是?和原始问题一样,我们用Q[i,j]表示用前i个砝码是否能称量重量为j的物品。Q[i,j]=1表示可以称量,Q[i,j]=0表示不能称量。那么状态转移方程为:

 Q[i,j]=Q[i-1,j] + Q[i-1,j-w[i]] + Q[i-1,j-2*w[i]]....+ Q[i-1,j-k*w[i]]

其中k=min{a[i],|j/w[i]|}(向下取整),边界条件为:Q[0,0]=1,如何能列出每种组合呢?



转载于:https://www.cnblogs.com/kevinLee-xjtu/archive/2011/12/10/2299092.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值