poj 1882 背包(从1开始的连续邮票数量)

题意:给出一个上限硬币数量s,给出n套硬币价值,求一套硬币能用不大于s数量的硬币组成从1开始连续的区间价值,其中,如果其最大值相同,输出数量小的,如果再相同,输出最大价值小的。

思路:背包。显然要枚举硬币价值。一开始思路是开bool数组记录能否达到,再开used数组记录达到当前总价值的最小所需硬币数量;后来发现只用used一个数组即可。后来参考了另一个人的写法发现还可以优化成完全背包的类似写法。下面贴优化前和后的代码。

#include <stdio.h>
#include <string.h>
#define INF 0x3fffffff
#define N 1005
int n,m,T;
int a[15],used[N];
int res[3],out[15];//0表示最优cover值,1表示硬币数量,2表示最大硬币面值
int main(){
	//freopen("a.txt","r",stdin);
	while(scanf("%d",&n) && n){
		int i,j,max;
		res[1] = res[2] = INF;
		res[0] = 0;
		scanf("%d",&T);
		while(T--){
			memset(used,0,sizeof(used));
			scanf("%d",&m);
			for(i = 1;i<=m;i++){
				scanf("%d",&a[i]);
				used[a[i]] = 1;
			}
			for(i = 1;i<=m;i++)
				for(j = a[i]+1;j<=n*100;j++){
					if(!used[j] && used[j-a[i]] && used[j-a[i]]<n){
						used[j] = 1;
						used[j] = used[j-a[i]]+1;
					}else if(used[j-a[i]] && used[j-a[i]]<n && used[j-a[i]]+1<used[j])
						used[j] = used[j-a[i]]+1;
				}
			max = 0;
			for(i = 1;i<=n*100;i++){
				if(!used[i])
					break;
				max++;
			}
			if(max>res[0] || (max==res[0]&&m<res[1]) || (max==res[0]&&m==res[1]&&a[m]<res[2])){
				res[0] = max;
				res[1] = m;
				res[2] = a[m];
				for(j = 1;j<=m;j++)
					out[j] = a[j];
			}
		}
		printf("max coverage = %d : ",res[0]);
		for(i = 1;i<=res[1];i++)
			printf("%d ",out[i]);
		putchar('\n');
	}
	return 0;
}

used用n+1初始化,找最大cover数量时碰到n+1则停止。比较巧妙!

#include <stdio.h>
#include <string.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define INF 0x3fffffff
#define N 1005
int n,m,T;
int a[15],used[N];
int res[3],out[15];//0表示最优cover值,1表示硬币数量,2表示最大硬币面值
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d",&n) && n){
		int i,j,max;
		res[1] = res[2] = INF;
		res[0] = 0;
		scanf("%d",&T);
		while(T--){
			for(i = 1;i<=100*n;i++)
				used[i] = n+1;
			used[0] = 0;
			scanf("%d",&m);
			for(i = 1;i<=m;i++)
				scanf("%d",&a[i]);
			for(i = 1;i<=m;i++)
				for(j = a[i];j<=n*100;j++)
					used[j] = min(used[j],used[j-a[i]]+1);
			max = 0;
			for(i = 1;i<=n*100;i++){
				if(used[i] == n+1)
					break;
				max++;
			}
			if(max>res[0] || (max==res[0]&&m<res[1]) || (max==res[0]&&m==res[1]&&a[m]<res[2])){
				res[0] = max;
				res[1] = m;
				res[2] = a[m];
				for(j = 1;j<=m;j++)
					out[j] = a[j];
			}
		}
		printf("max coverage = %d : ",res[0]);
		for(i = 1;i<=res[1];i++)
			printf("%d ",out[i]);
		putchar('\n');
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值