又是一道经典搜索题,剪枝是必须的!
题意有点晦涩,参考了网上提供的中文版才明白过来到底是个什么事。
具体题意可参考http://blog.csdn.net/sdj222555/article/details/7240991这篇博文,同时也给出了用经典DFS模板写的代码。
这里给出了个人所想的剪枝方法:
1. 重点剪枝:由于题目中说明了不同类型的邮票可能具有相同的面值,所以这里把首先把邮票按面值大小进行排序,然后只保留面值不同的邮票,以及邮票面值相同时的邮票种类数。搜索组合方案时,暂时只考虑面值大小,不考虑邮票种类。
如邮票种类:1 1 1 1 2 2 2 3 3 就可以表示为:(1, 4), (2, 3), (3, 2);
要求找到面值总额为5的组合,那么搜索会考虑到这个组合:面值为1的需要3张,面值2的需要1张。事实上,面值1的邮票种类数有4张,为了使得最后的邮票种类数达到最大,应该使得组合中的3张面值为1的邮票种类不同。
2. 所需的面值总额小于最小的邮票面值时,停止
3. 邮票张数超过4,时,停止
这三个剪枝中,2,3是应题目要求,只有1是需要自己拿脑子来想想的。
附代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Answer
{
public:
int types;
int number;
int maxValue;
vector<pair<int,int> > ans;
Answer(int t=0, int n=0, int m=0):types(t), number(n), maxValue(m){
};
void assign(const vector<pair<int,int> >& temp,
const vector<pair<int,int> >& stamps)
{
int i = 0;
while( temp[i].second && i < temp.size() )
{
int j = temp[i].first;
types += min(stamps[j].second, temp[i].second);
number += temp[i].second;
ans.push_back(pair<int, int>(j, temp[i].second));
++i;
}
maxValue = stamps[ans.back().first].first;
}
int replace(const Answer& temp)
{
if(temp.types == types
&& temp.number == number
&& temp.maxValue == maxValue)
return 0;
if(types > temp.types)
return 1;
else if(types < temp.types)
{
*this = temp;
return -1;
}
if(number < temp.number)
return 1;
else if(number > temp.number)
{
*this = temp;
return -1;
}
if(maxValue > temp.maxValue)
return 1;
else if(maxValue < temp.maxValue)
{
*this = temp;
return -1;
}
}
void clear()
{
types = number = maxValue = 0;
ans.clear();
}
};
vector<pair<int, int> > stamps;
Answer answer;
vector<pair<int, int> > temp_ans;
bool first, tie;
void Work(int level, int pos, int need, int num)
{
if( !need )
{
/*
cout << "--------------------" << endl;
for(int i = 0; i < temp_ans.size(); ++i)
{
int j = temp_ans[i].first;
cout << stamps[j].first << " " << temp_ans[i].second << endl;
}
cout << "--------------------" << endl;
*/
//a solution
if( first )
{
first = false;
answer.assign(temp_ans, stamps);
}
else
{
Answer temp ;
temp.assign(temp_ans, stamps);
int r = answer.replace( temp );
if( !r )
tie = true;
else if(tie && r == -1)
tie = false;
}
return ;
}
if(num <= 0 || pos >= stamps.size() || level >= 4)
return ;
if(need < stamps[pos].first)
return;
int n = min(need/stamps[pos].first, num);
for(int i = 0; i <= n; ++i)
{
int j = i>0?1:0;
temp_ans[level] = pair<int,int>(pos, i);
Work(level+j, pos+1, need-stamps[pos].first*i, num-i);
temp_ans[level] = pair<int,int>(0, 0);
}
}
void Preprocess(vector<int>& v)
{
sort(v.begin(), v.end());
stamps.clear();
stamps.push_back( pair<int, int>(v[0], 1) );
int k = 0;
for(int i = 1; i < v.size(); ++i)
{
if(stamps[k].first != v[i])
{
stamps.push_back( pair<int,int>(v[i], 1) );
++k;
}
else stamps[k].second += 1;
}
}
void JudgeTie()
{
vector<pair<int,int> > v = answer.ans;
for(int i = 0; i < v.size(); ++i)
{
int pos = v[i].first, num = v[i].second;
int types = stamps[pos].second;
if(types > 1 && num != types)
{
tie = true;
break;
}
}
}
int main()
{
int stamp;
while( cin >> stamp )
{
vector<int> v;
while( stamp )
{
v.push_back( stamp );
cin >> stamp;
}
Preprocess( v );
int minDemand = v.front();
int maxDemand = v.back()*4;
int demand;
while( cin >> demand && demand )
{
if( demand < minDemand || demand > maxDemand )
{
cout << demand << " ---- none" << endl;
continue;
}
first = true;
tie = false;
temp_ans.assign(4, pair<int,int>(0, 0));
answer.clear();
Work(0, 0, demand, 4);
if( first )
{
cout << demand << " ---- none" << endl;
continue;
}
JudgeTie();
if( tie )
{
cout << demand << " (" << answer.types << "): tie" << endl;
continue;
}
cout << demand << " (" << answer.types << "):";
vector<pair<int, int> > v = answer.ans;
for(int i = 0; i < v.size(); ++i)
{
for(int k = 0; k < v[i].second; ++k)
cout << " " << stamps[v[i].first].first;
}
cout << endl;
}
}
return 0;
}