PKU ACM 1010 STAMPS 邮票

这篇博客探讨了如何解决ACM竞赛中的邮票问题,即如何在限制邮票种类和数量的前提下,找到满足特定总面值的邮票组合。文章介绍了题意,提出了按面值排序的剪枝策略,以及一种简化评价方案的计算公式,并分享了作者的解决方案,邀请读者分享更好的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:STAMPS

Description

Have you done any Philately lately?

You have been hired by the Ruritanian Postal Service (RPS) to design their new postage software. The software allocates stamps to customers based on customer needs and the denominations that are currently in stock.

Ruritania is filled with people who correspond with stamp collectors. As a service to these people, the RPS asks that all stamp allocations have the maximum number of different types of stamps in it. In fact, the RPS has been known to issue several stamps of the same denomination in order to please customers (these count as different types, even though they are the same denomination). The maximum number of different types of stamps issued at any time is twenty-five.

To save money, the RPS would like to issue as few duplicate stamps as possible (given the constraint that they want to issue as many different types). Further, the RPS won't sell more than four stamps at a time.

Input

The input for your program will be pairs of positive integer sequences, consisting of two lines, alternating until end-of-file. The first sequence are the available values of stamps, while the second sequence is a series of customer requests. For example:

1 2 3 0 ; three different stamp types
7 4 0 ; two customers
1 1 0 ; a new set of stamps (two of the same type)
6 2 3 0 ; three customers

Note: the comments in this example are *not* part of the data file; data files contain only integers.

Output

For each customer, you should print the "best" combination that is exactly equal to the customer's needs, with a maximum of four stamps. If no such combination exists, print "none".
The "best" combination is defined as the maximum number of different stamp types. In case of a tie, the combination with the fewest total stamps is best. If still tied, the set with the highest single-value stamp is best. If there is still a tie, print "tie".

For the sample input file, the output should be:

7 (3): 1 1 2 3
4 (2): 1 3
6 ---- none
2 (2): 1 1
3 (2): tie

That is, you should print the customer request, the number of types sold and the actual stamps. In case of no legal allocation, the line should look like it does in the example, with four hyphens after a space. In the case of a tie, still print the number of types but do not print the allocation (again, as in the example).Don't print extra blank at the end of each line.

Sample Input

1 2 3 0	; three different stamp types
7 4 0		; two customers
1 1 0		; a new set of stamps (two of the same type)
6 2 3 0	; three customers

Sample Output

7 (3): 1 1 2 3 
4 (2): 1 3 
6 ---- none
2 (2): 1 1
3 (2): tie

这题第一个难点就是读题了,看了很久又参考了别人翻译的才弄明白,这里抄过来~

题意:
  给出n种邮票,每种邮票有自己的面值(面值可能重复)
  指定m种“总面值”,对每种“总面值”,求解满足如下条件的组合以达到该“总面值”
(1) 所用邮票在n种中可以重复选取
(2) 所用邮票张数〈=4
(3) 尽量多的使用那个不同种类的邮票 Max (Stamp Types)
(4) 若有多种方案满足(3),则选取张数最小的一种方案 Min (Stamp Num)
(5) 若有多种方案满足(3)(4),则选取“最大面额”最高的一种方案。 Max(Heightest Value)
(6) 若有多种方案满足(3)(4)(5) 则输出 “tie”


主要思路:

1. 先对输入按面值的从小到大排序,这对剪枝有帮助,比如:

    输入面值:1,2,3,4,5        顾客要求是 3

    假如计算到分支 1,1,2 的时候,超过 3 了,后面的 1,1,3  ;  1,1,4  ;  1,1,5 都不需要计算了

2. 对解的评价,采用了一个计算公式,省去了较复杂的条件判断

     评价分数 = 种类数 * 100 + (4 - 邮票数) * 10 + 最大面值;

3. 对解中邮票的种类数,这个不太好弄

     我用了个数组,记录序号,这个方法有点笨拙,大家有好的方法告诉我吧,吼吼~


其实我的方法类似于穷举了,一开始想用广度优先,总之大家有什么高见告诉我吧。

下面是源代码:

#include <iostream>
#include <algorithm>
using namespace std;

int stamps[100];	//存放邮票的种类(有人说只分配 25 种会不过)
int result[4];		//存放最终结果
int curresult[4];	//存放当前结果
int goal;			//顾客要求的面值
int num;			//邮票种类数
bool tie = false;	//判断是否有tie
int score = 0;		//最优解的分数
int curscore = 0;	//当前解的分数
int cards = 0;		//最优解中有多少张邮票
int curcards = 0;	//当前解中有多少张邮票
int kinds[4];		//记录类型的数组
int kindsnum = 0;	//最优解中有多少种邮票
int curkindsnum = 0;//当前解中有多少种邮票


void DFS(int cur, int index, int curcards)
{
	if((curcards > 3) && (cur != goal))	return;	//失败了,剪枝

	if(cur == goal)		//成功了,开始评分
	{
		curkindsnum = 1;
		for(int i = 1; i < curcards; i++)
		{
			if(kinds[i] != kinds[i-1])
				curkindsnum++;
		}
		curscore = curkindsnum * 100 + (4 - curcards) * 10 + stamps[index];

		if(curscore == score)
		{
			tie = true;
		}
		else if(curscore > score)
		{
			tie = false;
			score = curscore;
			cards = curcards;
			kindsnum = curkindsnum;
			for(int i = 0; i < 4; i++)
				result[i] = curresult[i];
		}
		return;
	}

	for(int i = index; i < num; i++)
	{
		if(cur + stamps[i] > goal)	return;		//剪枝
		curresult[curcards] = stamps[i];
		kinds[curcards] = i;
		DFS(cur + stamps[i], i, curcards + 1);
	}
	return;
}

void Print()
{
	if(!result[0])
	{
		cout << goal << " ---- none" << endl;
		return;
	}

	if(tie)
	{
		cout << goal << " (" << kindsnum << "): tie" << endl;
		return;
	}
	else
	{
		cout << goal << " (" << kindsnum << "): ";
		for(int i = 0; i < cards; i++)
			cout << result[i] << " ";
	}
	cout << endl;
}

int main()
{
	while(true)
	{
		memset(stamps, 0, sizeof(stamps));
		num = 0;
		
		cin >> stamps[0];
		if(!stamps[0])	break;

		while(stamps[num])
		{
			cin >> stamps[++num];
		}
		

		sort(stamps, stamps + num);		//从小到大排序

		while(true)
		{
			memset(result, 0, sizeof(result));
			cin >> goal;
			if(goal == 0)	break;
			if(goal > 4 * stamps[num-1])	//加个判断,不过应该没什么作用
			{
				cout << goal << " ---- none" << endl;
				continue;
			}

			score = 0;
			DFS(0, 0, 0);
			Print();
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值