POJ1010-STAMPS

 

转载请注明出处:優YoU   http://user.qzone.qq.com/289065406/blog/1311305814

大致题意:

题意比较难懂。大致如下:

第一行数字是邮票的面值,每一个数字就是一个不同的种类,哪怕面值相同。以0结束。

第二行数字是顾客所需要的邮票总面值。每个数字就是一个顾客的需求,以0结束。

每两行是一组case。以EOF结束输入。

 

顾客是集邮爱好者,所以你必须尽可能的给他不同种类的邮票。

但是一位顾客最多只能拿4张邮票。

显然,我们拥有的邮票就是第一行中的数据。

 

解题思路:

DFS寻找所有的解,再逐一比较寻找最优解,剪枝是关键。

 

关于tie。

满足顾客需求的解就是可行解。

邮票种类最多的可行解为最优。

如果存在两个以上的最优解的邮票种类是一样的,张数最少的更优

张数也一样的话,这些最优解中最大面值较大的更优。

若邮票种类、张数、最大面值三者都分别相同,则认为这些最优解相同,输出tie。

没有解就是none。

 

做法大致有三种。

可以去参考这位牛人的三种方法:http://blog.csdn.net/cugbliang/article/details/2742242

 

1、枚举。反正最多拿4张,可以4重循环暴搜最优解。

2、DFS。每次搜索后,如果有解,更新最优解,关键在剪枝。

3、三维DP。这个没怎么研究,不太懂……参考上面的网址吧。。

 

我用的是第二种DFS

 

剪枝:

1、最多拿四张邮票,如果同一面值的邮票种类超过5,以5计算。

为什么不以4计算呢?因为tie

2、若DFS的深度超过了4,那么就返回。(最多四张邮票)

3、技巧剪枝:

先对输入的邮票按面值升序排序,DFS到面值k时,不再搜索面值<k的邮票。

   同时排序也是为了保证DFS的最优解的邮票种类最多。

 

Source修正:

Pacific Northwest 1998

http://www.cs.berkeley.edu/~hilfingr/programming-contest/pacific-northwest/1998/ContestProblems/

 

//Memory Time 
// 228K   0MS 

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

const int size=26;

int value[size];  //第i种邮票面值value[i]
int pv;  //value[]指针
int time[size];  //标记第i种邮票被分配过的次数

bool flag;  //标记是否已经出现过解
bool flag_tie;  //标记是否为tie
int solve[6];  //solve[0]:邮票张数  solve[5]:邮票种数  solve[1..4]:持有的邮票面值,0表示不持有
int BestSolve[6];  //最优解

void dfs(int need,int num,int type,int pre);  //need:总面值 num:邮票张数  type:邮票种数
int max4(int* s);  //返回s[1..4] 4个数中的最大值
void best(int num,int type);  //更新最优解

int main(void)
{
	while(true)
	{
		pv=0;
		int type[size]={0};  //面值为i的邮票的种数type[i]

		int tmp;
		while(true)
		{
			if(scanf("%d",&tmp)==EOF)
				exit(0);
			if(tmp==0)
				break;

			if(type[ tmp ]<5)     //剪枝,同面额的邮票种类超过5,则按5计算
			{
				type[ tmp ]++;
				value[pv++]=tmp;
			}
		}
		sort(value,value+pv);  //要使分配的邮票的种类尽可能多
		                       //只需在搜索前把邮票面值升序排序,从最小面额开始搜索

		int need;  //顾客需求
		while(cin>>need && need)
		{
			flag=false;
			flag_tie=false;
			memset(solve,0,sizeof(solve));
			memset(BestSolve,0,sizeof(BestSolve));
			memset(time,0,sizeof(time));

			/*Search*/

			dfs(need,0,0,0);

			/*Output*/

			cout<<need;
			if(BestSolve[0]==0)
				cout<<" ---- none"<<endl;
			else
			{
				cout<<" ("<<BestSolve[5]<<"):";

				if(flag_tie)
					cout<<" tie"<<endl;
				else
				{
					sort(BestSolve+1,BestSolve+5);
					for(int i=1;i<=4;i++)
					{
						if(BestSolve[i]==0)
							continue;
						cout<<' '<<BestSolve[i];
					}
					cout<<endl;
				}
			}
		}
	}
	return 0;
}

void dfs(int need,int num,int type,int pre)  //need:总面值 num:邮票张数  type:邮票种数
{
	if(num==5)  //剪枝,顾客持有邮票张数不超过4
		return;

	if(need==0)
	{
		if(!flag)
		{
			if(type==BestSolve[5])  //最优解的种类type相同
			{
				if(num==BestSolve[0])  //最优解的张数num相同
				{
					int Maxs=max4(solve);  //solve的最大面值
					int MaxBs=max4(BestSolve); //BestSolve的最大面值

					if(Maxs==MaxBs)    //存在多个最优解
						flag_tie=true;
					else if(Maxs>MaxBs)  //种类、张数都相同的情况下,最大面值较大的解优先
					{
						flag_tie=false;
						best(num,type);
					}
				}
				else if(num<BestSolve[0])  //种类相同情况下,张数少的解优先
				{
					flag_tie=false;
					best(num,type);
				}
			}
			else if(type>BestSolve[5])  //种类多的解优先
			{
				flag_tie=false;
				best(num,type);
			}
		}
		else
		{
			flag=true;
			best(num,type);
		}
		
		return;
	}

	for(int i=pre;i<pv;i++)   //i=pre 剪枝,不重复搜索比当前面值小的邮票,同时避免错误的tie
	{
		if(need>=value[i])
		{
			solve[num+1]=value[i];

			if(time[i]!=0)
			{
				time[i]++;
				dfs(need-value[i],num+1,type,i);
			}
			else
			{
				time[i]++;
				dfs(need-value[i],num+1,type+1,i);
			}

			solve[num+1]=0;  //回溯
			time[i]--;
		}
		else
			return;  //value已排序
	}

	return;
}

int max4(int* s)  //返回s[1..4] 4个数中的最大值
{
	int a=s[1]>s[2]?s[1]:s[2];
	int b=s[3]>s[4]?s[3]:s[4];

	return a>b?a:b;
}

void best(int num,int type)  //更新最优解
{
	BestSolve[0]=num;
	BestSolve[5]=type;

	for(int k=1;k<=4;k++)
		BestSolve[k]=solve[k];
	return;
}


 

 

Sample Input

1 2 3 0
7 4 0  
1 1 0
6 2 3 0
1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0
2 8 0
1 2 3 4 5 6 7 8 9 10 0
2 5 10 20 33 0
1 1 6 0
4 5 0
 
Sample Output

7 (3): 1 1 2 3
4 (2): 1 3
6 ---- none
2 (2): 1 1
3 (2): tie
2 (2): 1 1
8 (4): tie
2 (1): 2
5 (2): 1 4
10 (4): 1 2 3 4
20 (4): tie
33 (4): 6 8 9 10
4 (2): tie
5 ---- none

 

 

 

你可能感兴趣的:(search,input,output)