HDU2844Coins(多重背包)

关于多重背包,可以参考这篇文章,很详细http://blog.csdn.net/flyinghearts/article/details/5898183

多重背包问题:有n种物品和容量为v的背包,第i件物品的容量为v[i],价值为w[i],数量为n[i]件。怎样装才能是背包容纳最大的价值。

多重背包用单调队列优化的模板:

#include <stdio.h>
#include <string.h>

const int N = 250005;
const int N2 = 1005;
int V[N2], W[N2], M[N2];//V:容量 W:价值 M:数量
int Q1[N], Q2[N];
int f[N];
int main(){
	int n, m;
	while(scanf("%d%d", &n, &m) != EOF){
		for(int i = 0; i < n; i++){
			scanf("%d%d%d", &V[i], &W[i], &M[i]);
		}
		for(int i = 0; i <= m; i++)
			f[i] = 0;
		for(int i = 0; i < n; i++){
			for(int j = 0; j < V[i]; j++){
				int head1 = 0, tail1 = -1;
				int head2 = 0, tail2 = -1;
				for(int k = j, cnt = 0; k <= m; k += V[i], cnt ++){
					if(tail1 - head1 == M[i]){
						if(Q1[head1] == Q2[head2])
							head2 ++;
						head1++;
					}
					int tmp = f[k] - cnt * W[i];
					Q1[++tail1] = tmp;
					while(tail2 >= head2 && Q2[tail2] < tmp) --tail2;
					Q2[++tail2] = tmp;
					f[k] = Q2[head2] + cnt * W[i];
				}
			}
		}
		int ans = f[m];
		printf("%d\n", ans);
	}
	return 0;
}

题目:

HDU2844

题意:有n种硬币,每种价值为a[i],数量为c[i],问在价值不超过m的情况下,一共有几种组合方式。

解法1:

直接套用使用多重背包模板,不过初始化时将f[0]初始化为0,其他的初始化为无穷小。最后统计f数组大于0的元素数目。不过需要减枝,将数目为1的物品用01背包做,将总价值超过m的用完全背包做。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int INF = -100005;
const int M = 100005;
const int N = 105;
int Q1[M], Q2[M];
int a[M], c[M];
int f[M];
int main(){
	int n, m;
	while(scanf("%d%d", &n, &m) && (n + m)){
    for(int i = 0; i < n; i++)
      scanf("%d", &a[i]);
    for(int i = 0; i < n; i++)
      scanf("%d", &c[i]);
    for(int i = 0; i <= m; i++)
      f[i] =  INF;
    f[0] = 0;
    for(int i = 0; i < n; i++){
      if(c[i] == 1){
        for(int j = m; j >= a[i]; j--)
          f[j] = max(f[j], f[j-a[i]] + a[i]);
      }
      else if(c[i] * a[i] > m){
        for(int j = a[i]; j <= m; j++)
          f[j] = max(f[j], f[j-a[i]] + a[i]);
      }else{
        for(int j = 0; j < a[i]; j++){
          int head1 = 0, tail1 = -1;
          int head2 = 0, tail2 = -1;
          for(int k = j, cnt = 0; k <= m; k+=a[i], cnt++){
            if(tail1 - head1 == c[i]){
              if(Q2[head2] == Q1[head1])head2 ++;
              head1 ++;
            }
            int tmp = f[k] - cnt * a[i];
            Q1[++tail1] = tmp;
            while(tail2 >= head2 && Q2[tail2] < tmp) --tail2;
  					Q2[++tail2] = tmp;
  					f[k] = Q2[head2] + cnt * a[i];
          }
        }
      }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++)
      if(f[i] >= 0)
        ans ++;
    printf("%d\n", ans);
	}
	return 0;
}

解法2:

这种方法比第一种方法代码量小,有点类似于完全背包的方法,不同之处在于用一个cnt[]数组来记录第i种物品已经使用了几个,当达到本物品最大数目的时候,就不能再使用。

代码:

#include <stdio.h>
#include <string.h>
const int M = 100005;
const int N = 105;
bool tag[M];
int a[M], c[M];
int cnt[M];
int main(){
	int n, m;
	while(scanf("%d%d", &n, &m) && (n + m)){
		memset(tag, 0, sizeof(tag));
		tag[0] = true;
		for(int i = 0; i < n; i++)
			scanf("%d", &a[i]);
		for(int i = 0; i < n; i++)
			scanf("%d", &c[i]);
		for(int i = 0; i < n; i++){
			memset(cnt, 0, sizeof(cnt));
			for(int j = a[i]; j <= m; j++){
				if(!tag[j] && tag[j-a[i]] && cnt[j-a[i]]<c[i]){
					cnt[j] = cnt[j-a[i]] + 1;
					tag[j] = true;
				}
			}
		}
		int ans = 0;
		for(int i = 1; i <= m; i++)
			if(tag[i])ans ++;
		printf("%d\n", ans);
	}
	return 0;
}
HDU1171

因为价值和容量是相同的,所以同样可以用以上两种方法:

#include <stdio.h>
#include <string.h>

const int N = 250005;
const int N2 = 1005;
int V[N2], M[N2];
int Q1[N], Q2[N];
int f[N];
int main(){
	int n, Sum;
	while(scanf("%d", &n) && n >= 0){
		Sum = 0;
		for(int i = 0; i < n; i++){
			scanf("%d%d", &V[i], &M[i]);
			Sum += V[i] * M[i];
		}
		int S = Sum >> 1;
		for(int i = 0; i <= S; i++)
			f[i] = 0;
		for(int i = 0; i < n; i++){
			for(int j = 0; j < V[i]; j++){
				int head1 = 0, tail1 = -1;
				int head2 = 0, tail2 = -1;
				for(int k = j, cnt = 0; k <= S; k += V[i], cnt ++){
					if(tail1 - head1 == M[i]){
						if(Q1[head1] == Q2[head2])
							head2 ++;
						head1++;
					}
					int tmp = f[k] - cnt * V[i];
					Q1[++tail1] = tmp;
					while(tail2 >= head2 && Q2[tail2] < tmp) --tail2;
					Q2[++tail2] = tmp;
					f[k] = Q2[head2] + cnt * V[i];
				}
			}
		}
		int ans2 = f[S], ans1 = Sum - ans2;
		printf("%d %d\n", ans1, ans2);
	}
	return 0;
}
#include <stdio.h>
#include <string.h>

const int N = 250005;
const int N2 = 1005;
int V[N2], M[N2];
int cnt[N];
bool tag[N];
int main(){
	int n, Sum;
	while(scanf("%d", &n) && n >= 0){
		Sum = 0;
		for(int i = 0; i < n; i++){
			scanf("%d%d", &V[i], &M[i]);
			Sum += V[i] * M[i];
		}
		int S = Sum >> 1;
		memset(tag, false, sizeof(tag));
		tag[0] = true;
		for(int i = 0; i < n; i++){
			memset(cnt, 0, sizeof(cnt));
			for(int j = V[i]; j <= S; j++){
				if(!tag[j] && tag[j-V[i]] && cnt[j - V[i]] < M[i]){
					tag[j] = true;
					cnt[j] = cnt[j-V[i]] + 1;
				}
			}
		}
		int ans1, ans2;
		for(int i = S; i >= 0; i--){
			if(tag[i]){
				ans2 = i;
				break;
			}
		}
		ans1 = Sum - ans2;
		printf("%d %d\n", ans1, ans2);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值