POJ_1010_STAMPS

本文介绍了一种解决邮票问题的算法实现,通过递归和暴力搜索两种方式寻找最佳组合,以达到指定的总面值。算法考虑了不同类型邮票的最大数量限制,并通过排序优化了邮票类型的输入。
//用MT[k][n][v] (k>=0, 0<=n<=4, v>=0) 表示从前k个类型中,最多取n张,总面值为v的所有组合中最大的类型数。则有递推方程:
//MT[k][n][v] = MAX{ MT[k-1][n-i][v-i*Values[k-1]] + (i>0?1:0) }  (0<=i<=n, v>=i*Values[k-1]) 
#include<iostream>
#include<cstdio>
#include<climits>

using namespace std;

#define STAMP_ARR_L 100
#define COMB_ARR_L 4

int s_arr[STAMP_ARR_L], s_cnt, customer;//邮票类型数组、邮票类型数目、顾客需求
int best_comb_p[COMB_ARR_L], max_type, min_num, max_val, flag_tie;//最佳组合、最大类型数、最小邮票数、最大单面值
int comb_p[COMB_ARR_L], m_t, m_n, m_v, sum;//当前枚举组合、类型数、邮票数、最大面值

void sort(int arr[], int &cnt){
	int i, j, k, last;
	for (i = 0; i < cnt - 1; i++)
		for (j = cnt - 1; j > i; j--)
			if (arr[j] < arr[j - 1])
				arr[j] ^= arr[j - 1] ^= arr[j] ^= arr[j - 1];

	i = 0; j = 0; last = -1;
	while (j < cnt){//当同面值邮票多于5张时没有用
		if (arr[j] == last){
			if (k == 5){
				j++;
			} else{
				arr[i++] = arr[j++];
				k++;
			}
		} else{
			last = arr[j];
			arr[i++] = arr[j++];
			k = 1;
		}
	}

	cnt -= j - i;
}

void updateBestComb(int num){
	int i, j;

	m_n = COMB_ARR_L - num;
	for (i = 1, j = 1; i < m_n; i++)
		if (comb_p[i] != comb_p[i - 1])
			j++;
	m_t = j;
	m_v = comb_p[m_n - 1];

	if (m_t == max_type && m_n == min_num && m_v == max_val) flag_tie = 1;
	if ((m_t > max_type) ||
		(m_t == max_type && m_n < min_num) ||
		(m_t == max_type && m_n == min_num && m_v > max_val)){
		for (i = 0; i < m_n; i++) best_comb_p[i] = comb_p[i];
		max_type = m_t;
		min_num = m_n;
		max_val = m_v;
		flag_tie = 0;
	}
}

//在邮票数组s_arr中,从s_arr_p位置开始,向后选择num张邮票,使其总面值为customer
void findBestComb(int s_arr_p,int customer,int num){
	if (customer == 0 && s_arr_p < s_cnt && num >= 0){
		updateBestComb(num);
	} else if (customer > 0 && num > 0){
		for (int i = s_arr_p; i < s_cnt; i++){
			comb_p[COMB_ARR_L - num] = i;
			findBestComb(i, customer - s_arr[i], num - 1);
		}
	}
	return;
}

int main(){
	while (true){
		s_cnt = 0;
		while (cin >> s_arr[s_cnt], s_arr[s_cnt]) s_cnt++;
		if (cin.fail()) break;

		sort(s_arr, s_cnt);

		while (cin >> customer, customer){
			max_type = 0; min_num = INT_MAX; max_val = 0; sum = 0; flag_tie = 0;
			findBestComb(0, customer, COMB_ARR_L);

			if (max_type == 0){
				printf("%d ---- none\n", customer);
			} else if (flag_tie){
				printf("%d (%d): tie\n", customer, max_type);
			} else{
				printf("%d (%d):", customer, max_type);
				for (int i = 0; i < min_num; i++)
					printf(" %d", s_arr[best_comb_p[i]]);
				printf("\n");
			}
		}
	}
	return 0;
}

//暴搜版本
#include<iostream>
#include<cstdio>
#include<climits>

using namespace std;

void sort(int arr[], int &cnt){
	int i, j, k, last;
	for (i = 0; i < cnt - 1; i++)
		for (j = cnt - 1; j > i; j--)
			if (arr[j] < arr[j - 1])
				arr[j] ^= arr[j - 1] ^= arr[j] ^= arr[j - 1];

	i = 0; j = 0; last = -1;
	while (j < cnt){//当同面值邮票多于5张时没有用
		if (arr[j] == last){
			if (k == 5){
				j++;
			} else{
				arr[i++] = arr[j++];
				k++;
			}
		} else{
			last = arr[j];
			arr[i++] = arr[j++];
			k = 1;
		}
	}

	cnt -= j - i;
}

int main(){
	int s_arr[100], s_cnt, customer;//邮票类型数组、邮票类型数目、顾客需求
	int best_comb[4], max_type, min_num, max_val, flag_tie;//最佳组合、最大类型数、最小邮票数、最大单面值
	int m_t, m_n, m_v, sum;//当前枚举情况下的类型数、邮票数、最大面值
	int i, j, k, l;

	while (true){
		s_cnt = 0;
		while (cin >> s_arr[s_cnt], s_arr[s_cnt]) s_cnt++;
		if (cin.fail()) break;

		sort(s_arr, s_cnt);

		while (cin >> customer, customer){
			max_type = 0; min_num = INT_MAX; max_val = 0; sum = 0; flag_tie = 0;
			//枚举所有情况
			for (i = 0; i <= s_cnt; i++)
				for (j = i; j <= s_cnt; j++)
					for (k = j; k <= s_cnt; k++)
						for (l = k; l <= s_cnt; l++){
							m_t = 0; m_n = 0; m_v = 0; sum = 0;
							if (i < s_cnt){
								m_t++;
								m_n++;
								m_v = s_arr[i];
								sum += s_arr[i];
								
								if (j < s_cnt){
									if (j != i) m_t++;
									m_n++;
									m_v = s_arr[j];
									sum += s_arr[j];

									if (k < s_cnt){
										if (k != j) m_t++;
										m_n++;
										m_v = s_arr[k];
										sum += s_arr[k];

										if (l < s_cnt){
											if (l != k) m_t++;
											m_n++;
											m_v = s_arr[l];
											sum += s_arr[l];
										}
									}
								}
							}

							if (sum == customer){
								if (m_t == max_type && m_n == min_num && m_v == max_val) flag_tie = 1;
								if ((m_t > max_type) ||
									(m_t == max_type && m_n < min_num) ||
									(m_t == max_type && m_n == min_num && m_v > max_val)){
									best_comb[0] = s_arr[i]; 
									best_comb[1] = s_arr[j]; 
									best_comb[2] = s_arr[k];
									best_comb[3] = s_arr[l];
									max_type = m_t;
									min_num = m_n;
									max_val = m_v;
									flag_tie = 0;
								} 
							} else continue;
						}

			if (max_type == 0){
				printf("%d ---- none\n", customer);
			} else if (flag_tie){
				printf("%d (%d): tie\n", customer, max_type);
			} else{
				printf("%d (%d):", customer, max_type);
				for (i = 0; i < min_num; i++)
					printf(" %d", best_comb[i]);
				printf("\n");
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值