POJ 1010 STAMPS

严格意义上说,这是一道搜索题。

是的,这真的是一道不带一点遮掩的“暴!力!搜!索!”

我居然没看出来,最后是用DP做完的。。。

转移方程参考的背包问题,

我用了一个二维数组solution[i][j]

记录用 i 张邮票组合成 j 需求的最优组合情况。

转移方程为

solution[i][j]=best(solution[i-k][j-stamp[x]*k])

时间复杂度估算为O(n*m),其中n为面额数量,m为最大需求。

空间复杂度估算为O(m),m为最大需求值。

幸亏这题数据比较仁慈,要不就MLE了。

不过话说回来,要是数据恶心人的话,裸搜O(n^4)怎么看都会TLE。

这题比较神奇的地方在于,题目居然没有给数据范围,这真是。。。

好了,上代码,没懂得看注释


#include <iostream>
using namespace std;

class Solution{
public:
	int type_num;				//邮票种类
	int stamp_num;				//邮票总数
	int max_stamp;				//最大面额
	int combination_num;		//等价组合的数量
	int stamp_combination[4];	//第一种组合
};

//flag记录两个方案的好坏,-1表示s方案更优,0表示相同,1表示更劣
int cmp(Solution s,int type_num,int stamp_num,int max_stamp)	{
	int flag;
	if (type_num>s.type_num)
		flag = 1;
	else if (type_num<s.type_num)
		flag = -1;
	else if (stamp_num<s.stamp_num)
		flag = 1;
	else if (stamp_num>s.stamp_num)
		flag = -1;
	else if (max_stamp>s.max_stamp)
		flag = 1;
	else if (max_stamp<s.max_stamp)
		flag = -1;
	else 
		flag = 0;

	return flag;
}

int main() {
	Solution * solution[5];		//解决方案数组,题中没范围。
	int n,need;					//n代表面额总数,need代表顾客需求
	int stamp[300];				//存储邮票面额,题中没给范围,试出来的。
	int i,j,k,l,x,flag,type_num,stamp_num,max_stamp;
	n=0;
	while (cin>>stamp[n]) {

		//读入邮票面额
		do	{
			n++;
			cin>>stamp[n];
		}
		while (stamp[n]);

		//排序,居然要排序,居然还有隐藏条件要排序!!!
		for (i=0;i<n;i++)	{
			j=i;
			while (stamp[j-1]<stamp[j] && j)	{
				k=stamp[j-1];
				stamp[j-1]=stamp[j];
				stamp[j]=k;
				j--;
			}
		}

		//处理用户需要
		while (cin>>need && need)	{
			//初始化
			for (i=0;i<5;i++)
				solution[i] = new Solution[need+1];
			for (i=0;i<=need;i++)
				for (j=0;j<5;j++)
					solution[j][i].combination_num=0;
			solution[0][0].type_num=0;
			solution[0][0].stamp_num=0;
			solution[0][0].max_stamp=0;
			solution[0][0].combination_num=1;

			for(i=0;i<n;i++)
				for (j=need;j>=stamp[i];j--)
					for (k=1;k<=4;k++)
						for (l=0;l+k<5;l++)
						if (j>=stamp[i]*k && solution[l][j-stamp[i]*k].combination_num) {
							type_num = solution[l][j-stamp[i]*k].type_num+1;
							stamp_num = solution[l][j-stamp[i]*k].stamp_num+k;
							max_stamp = (solution[l][j-stamp[i]*k].max_stamp<stamp[i])?(stamp[i]):(solution[l][j-stamp[i]*k].max_stamp);
							if (stamp_num>4)
								continue;

							//把新方案和原方案进行比较
							flag = cmp(solution[l+k][j],type_num,stamp_num,max_stamp);

							//按照flag更新组合方案
							if (flag==1)	{			//更新
								solution[l+k][j].type_num=type_num;
								solution[l+k][j].stamp_num=stamp_num;
								solution[l+k][j].max_stamp=max_stamp;
								solution[l+k][j].combination_num=solution[l][j-stamp[i]*k].combination_num;

								//更新组合方案
								for (x=0;x<solution[l][j-stamp[i]*k].stamp_num;x++)
									solution[l+k][j].stamp_combination[x]=solution[l][j-stamp[i]*k].stamp_combination[x];
								for (x=solution[l][j-stamp[i]*k].stamp_num;x<stamp_num;x++)
									solution[l+k][j].stamp_combination[x]=stamp[i];
/*
								cout<<j<<"("<<type_num<<")--"<<solution[l+k][j].combination_num<<"--"<<max_stamp<<endl;
								for (x=0;x<stamp_num;x++)
									cout<<solution[l+k][j].stamp_combination[x]<<"   ";
								cout<<endl;
*/
							}
							else if (flag==0)	{		//更新同解数量
								solution[l+k][j].combination_num++;
							}
							else if (flag==-1)	{		//舍弃该解
							}
						}

			//找出最优解
			j=0;
			type_num=0;
			stamp_num=0;
			max_stamp=0;
			for (i=4;i>0;i--)	{
				flag = cmp(solution[i][need],type_num,stamp_num,max_stamp);

				if (flag==-1)	{
					type_num = solution[i][need].type_num;
					stamp_num = solution[i][need].stamp_num;
					max_stamp = solution[i][need].max_stamp;
					j=i;
				}
			}
			flag = j;


			if (solution[flag][need].combination_num==1)	{
				cout<<need<<" ("<<solution[flag][need]. type_num<<"):";
				for (i=solution[flag][need].stamp_num-1;i>=0;i--)
					cout<<" "<<solution[flag][need].stamp_combination[i];
				cout<<endl;
			}
			else if (solution[flag][need].combination_num>1)
				cout<<need<<" ("<<solution[flag][need]. type_num<<"): tie"<<endl;
			else
				cout<<need<<" ---- none"<<endl;
		}

		for (i=0;i<5;i++)
			delete solution[i];
		n=0;
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值