北大ACAM(1010STAMPS)代码


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

/*
	编写程序对于邮票的组合执行组合 并挑选出最优解

	题目要求如下:

	要求输入表示邮票的面值若干 不超过 25 个 对于输入的邮票 即使面值相同,同样视为不同的邮票种类
	然后需要将该些邮票来进行组合并交给不同的客户,客户有一系列的数字组成
	要求将给出的邮票进行组合 每个组合不能超过4张邮票 尽量交给客户端不同种类的邮票
	1. 如果 存在不同种类相同的组合,那么按种类相同数量最少的最优  
	2. 如果 种类相同数量相同 那么单值最大的最优
	3. 如果 仍存在同等  那么打印tie
*/


const int MAX_DIF_TYPES = 25;		//最大的输入的不同种类
const int MAX_TYPES = 4;			//最大交给客户的种类数量

int nTotalStampCount;				//表示输入邮票的数量

struct BestCombination				//保存最优组合的信息
{
	int nMaxStampValue;				//最大的邮票面值
	int nStampType;				//最大的邮票不同种类
	int nCountComb;				//组合最少的邮票数量(在邮票不同种类相同的情况下)
	int mResultComb[MAX_TYPES];			//保存最优的结果集
	bool bFlagTie;						//是否平局
};

BestCombination  gCombResult;		//保存最优组合结果
int mAllStampValue[MAX_DIF_TYPES];	//保存所有的输入邮票面值

void GetBestCombResult(int tmpStampComb[],int &nCountStamp,int& nTypeStamp,int customVal,int start);
void UpdateBestComb(int tmpStampComb[],int &nCountStamp,int& nTypeStamp,int nMaxStampValue);
void CopyCombResult(int tmpStampComb[], int &nCountStamp, int& nTypeStamp, int nMaxStampValue);
int  GetMaxVaule(int mArray[], int nSize);
int main()
{
	vector<int> customVec;				//保存用户需求

	while (true)
	{
		int  tmp_stamp;		
		int dif_stamp[MAX_DIF_TYPES] = {0};			//输入的不同邮票
		nTotalStampCount = 0;
		while (true)
		{
			if (scanf_s("%d",&tmp_stamp) == EOF) exit(0);
			
			if (tmp_stamp <= 0 || nTotalStampCount >= MAX_DIF_TYPES)
				break;

			///相同面值的邮票数量大于等于5  设置为5 剪枝操作
			if (dif_stamp[tmp_stamp - 1] < 5)
			{
				dif_stamp[tmp_stamp - 1]++;
				mAllStampValue[nTotalStampCount++] = tmp_stamp;
			}

		}
		///输入邮票的种类完成 需要对数据进行处理了。
		sort(mAllStampValue, mAllStampValue + nTotalStampCount);		//对邮票面值进行从小到大的排序	

		///每次清空顾客数据
		customVec.clear();
		while (cin >> tmp_stamp && tmp_stamp)
		{
			customVec.push_back(tmp_stamp);			//录入客户需求
		}

		for (int i = 0; i < customVec.size(); ++i)
		{
			int tmpStampCombCount = 0;
			int tmpStampCombType = 0;
			int tmpStampComb[MAX_TYPES] = { 0 };

			///对每个用户都进行数据上的处理,获取最优解
			memset(&gCombResult, 0, sizeof(gCombResult));			//首先对数据进行初始化

			GetBestCombResult(tmpStampComb,tmpStampCombCount,tmpStampCombType,customVec[i],0);						//获取组合的最优解
			
			打印结果
			if (gCombResult.nStampType <= 0)
				cout << customVec[i] << " ---- none" << endl;
			else if (gCombResult.bFlagTie)
				cout << customVec[i] << " (" << gCombResult.nStampType << "): tie" << endl;
			else
			{
				cout << customVec[i] << " (" << gCombResult.nStampType << "):";

				for (int i = 0; i < gCombResult.nCountComb; ++i)
					cout << " " << gCombResult.mResultComb[i] ;
				cout << endl;
			}
		}

	}

	return 0;
}

递归 回溯法来计算出最优解
void GetBestCombResult(int tmpStampComb[], int& nCountStamp, int& nTypeStamp, int customVal, int start)
{
	if (customVal < 0)  return;			//组合失败的清空下 返回

	if (customVal == 0)			//找到了一种组合
	{
		UpdateBestComb(tmpStampComb, nCountStamp, nTypeStamp, GetMaxVaule(tmpStampComb, nCountStamp));
	}
	else
	{
		for (int i = start; i < nTotalStampCount; ++i)			//对于每一个面值的数字 都进行组合计算
		{
			if (mAllStampValue[i] > customVal || nCountStamp >= MAX_TYPES) break;		//无法进行适当的组合 ,剪枝操作

			if (i > start || nCountStamp <= 0)			//如果当i进行循环也就表示了进行了不同种类的组合 需要自增不同种类数
				++nTypeStamp;					
			tmpStampComb[nCountStamp++] = mAllStampValue[i];

			///回溯调用函数 函数返回时 运行之后的代码
			GetBestCombResult(tmpStampComb, nCountStamp, nTypeStamp, customVal - mAllStampValue[i], i);


			///重置状态
			if (i > start || nCountStamp == 1)	//该部分的依据是  如果是进行种类相同的组合 则回溯到种类0 如果是不同种类的组合 每次递减一个种类继续组合
				--nTypeStamp;							//这样能够求出对于要求的所有解
			--nCountStamp;
		}
	}
}

void UpdateBestComb(int tmpStampComb[], int & nCountStamp, int & nTypeStamp, int nMaxStampValue)
{
	///当前不同种类数大于之前种类数的时候
	if (nTypeStamp > gCombResult.nStampType)
	{
		CopyCombResult(tmpStampComb, nCountStamp, nTypeStamp, nMaxStampValue);
	}
	///当种类相同的时候
	else if (nTypeStamp == gCombResult.nStampType)
	{
		if (nMaxStampValue > gCombResult.nMaxStampValue)			//比较最大单值
			CopyCombResult(tmpStampComb, nCountStamp, nTypeStamp, nMaxStampValue);
		else if(nMaxStampValue == gCombResult.nMaxStampValue)
			gCombResult.bFlagTie = true;			//将标志位设置为真
	}
}

void CopyCombResult(int tmpStampComb[], int & nCountStamp, int & nTypeStamp, int nMaxStampValue)
{
	gCombResult.nCountComb = nCountStamp;
	gCombResult.nMaxStampValue = nMaxStampValue;
	gCombResult.nStampType = nTypeStamp;
	gCombResult.bFlagTie = false;

	for (int i = 0; i < nCountStamp; ++i)  gCombResult.mResultComb[i] = tmpStampComb[i];
}

int GetMaxVaule(int mArray[], int nSize)
{
	int max = 0;

	for (int i = 0; i < nSize; ++i)
		if (mArray[i] > max)
			max = mArray[i];
	return max;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

David_TD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值