代码写的有点繁琐,欢迎指正~
题目来源:赛码网
买糖果(京东2016实习生真题)
题目描述
某糖果公司专门生产儿童糖果,它最受儿童欢迎的糖果有A1、A2两个序列,均采用盒式包装。包装好的A1类糖果体积为一个存储单位,而包装好的A2类糖果体积正好是A1类的两倍。
这两类糖果之所以广受儿童欢迎,是因为糖果中含有公司独家研发的魔幻因子。A1或A2序列中的糖果,看起来包装可能是一样的,但因为其中的魔幻因子含量不同被细分为不同的产品。
临近传统节日,公司的糖果供不应求。作为一个精明的糖果分销商,小东希望能够借此大赚一笔,于是带着现金开着货车来公司提货。货车的容量是确定的,小东希望采购的糖果能够尽可能装满货车,且糖果的魔幻因子总含量最高。只要不超出货车容量,糖果总可以装入货车中。
输入
输入中有多组测试数据。每组测试数据的第一行有两个整数n和v,1<=n<=10^5, 1<=v<=10^9,n为可供选购糖果数量,v为货车的容量。
随后n行为糖果的具体信息,第一行编号为1,第二行编号为2,以此类推,最后一行编号为n。
每行包含两个整数ti和pi,1<=ti<=2, 1<=pi<=10^4,ti为糖果所属的序列,1为A1、2为A2,pi则是其中的魔幻因子含量。
输出
对每组测试数据,先在单独的一行中输出能采购的糖果中的魔幻因子最高含量,
之后在单独的行中按编号从小到大的顺序输出以空格分隔的糖果编号,
若有多组糖果组合均能满足要求,输出编号最小的组。
若没有糖果能够满足要求,则在第一行中输出0,第二行输出“No”。
思路
容易进入的误区是认为给定的区间过大,无法用一般的方法来做。而实际上10^9是可以接受的,求和的最大值是10^9 * 10^4 = 10^13,使用long long可以进行计算;队列中放入10^5项也是可以接受的。
本题只有两类编号,所以可以不使用01背包的方法。假设选取编号1的糖果X个,编号2的糖果Y个,
则X+2*Y<=v,即2个编号1的糖果和1个编号2的糖果占用的容积相等,每次要么选取两个编号1的糖果,要么选取1个编号2的糖果。
具体做法
用两个容器分别存储编号1的糖果和编号2的糖果,容器中需要存储的内容包括每种糖果的索引编号,魔幻因子,并且按照魔幻因子排序。所以考虑使用map<int ,vector<int>>来存储(注意:这里需要使用vector来存储每种糖果的索引编号,因为具有相同魔幻因子的糖果可能映射到多个索引编号,虽然题目中没有提到允许重复,但是更没有提到说不重复,所以今后需要注意重复因素,这一点是在运行测试数据时才意识到的)。
按照魔幻因子从大到小分别遍历两个map,每次从编号1的map中提取魔幻因子最高的两个糖果sum1=magic1+magic2,和编号2的map中魔幻因子最高的一个糖果sum2=magic3进行比较。如果sum1>sum2,则选取magic1和magic2;如果sum1<sum2,则选取magic3;如果sum1==sum2,需要判断三者的索引编号的前后关系,如果magic3最小,则选取magic3;否则选取magic1和magic2。
经过一次比较之后,v-=2。依次遍历到v==1或者v==0。
如果v==0,直接输出结果。
如果v==1,则有两种行为,一种是再放入一个编号1的糖果,另一种是取出最近一次放入的编号1的糖果,使用编号2的糖果进行替换。
所以这里还需要进行一次判断!!!如果编号1中还有糖果magic1,记录上一次放入的编号1的糖果prev,如果magic1+prev大于编号2的糖果,则选取magic1;如果小于,则选取编号2的糖果;如果等于,则判断三者索引编号的前后关系再做决定。
注意:v==1时仍需要做一次判断,切勿直接选取编号1的糖果进行填充!!
代码写的有点繁琐,欢迎指正~
#include <iostream>
#include <map>
#include <queue>
#include <stack>
//
using namespace std;
typedef long long LL;
//
int main()
{
int n;
LL v;
map<int, deque<int>>ones;//存储编号1的糖果,这里使用deque是因为每次需要提取前两个糖果
//,如果使用queue,有时候需要先pop一个,才能计算出第二个糖果的魔幻因子
map<int, deque<int>>twos;//存储编号2的糖果
priority_queue<int, vector<int>, greater<int>> result;//存储maxsum对应选取的糖果的索引编号
LL maxsum = 0;//选取糖果的最大魔幻因子总和
//注意,如果把这个cin >> n >> m里面去,本地可以运行成功,但是在赛码网上一直是出错的
while (cin >> n >> v)
{
ones.clear();
twos.clear();
maxsum = 0;
//记录输入参数
int type, magic;
int i = 1;
while (i <= n && cin >> type >> magic)
{
if (type == 1)
{
ones[magic].push_back(i);
}
else if (type == 2)
{
twos[magic].push_back(i);
}
i++;
}
if (v < 1)
{
cout << 0 << endl << "No" << endl;
continue;
}
map<int, deque<int>>::reverse_iterator oneindex1 = ones.rbegin();
map<int, deque<int>>::reverse_iterator oneindex2 = ones.rbegin();
//注意,这里需要判断一下ones是否为空,使用rend()++会报错的
if (oneindex1 != ones.rend() && (oneindex1->second).size() == 1) oneindex2++;
map<int, deque<int>>::reverse_iterator twoindex = twos.rbegin();
LL num = v;
//需要记录最近一次选取的编号1的糖果的magic值以及对应的索引编号
int prev[2] = { 0, 0 };
while (num > 1)
{
//情形1:ones至少还有两个值,twos至少还有一个值
if (oneindex2 != ones.rend() && twoindex != twos.rend())
{
bool isOneMove = true;
LL sum = oneindex1->first + oneindex2->first;
if (sum > twoindex->first) isOneMove = true;
else if (sum < twoindex->first) isOneMove = false;
else if (sum == twoindex->first)
{
//比较谁靠前
//如果两个index在同一个vector中,oneindex2的索引编号要专门提取出来
if (oneindex1->second.front() == oneindex2->second.front())
{
int index2 = oneindex1->second[1];
if (index2 > twoindex->second[0]) isOneMove = false;
}
else if (oneindex1->second.front() > twoindex->second.front()
|| oneindex2->second.front() > twoindex->second.front())
isOneMove = false;
}
if (isOneMove)//选取oneindex1和oneindex2
{
maxsum += sum;//把sum加入总和
//把这两个加入result中去
result.push(oneindex1->second.front());
oneindex1->second.pop_front();
//记录最近一次选取的编号1的糖果的magic值以及对应的索引编号
prev[0] = oneindex2->first;
prev[1] = oneindex2->second.front();
result.push(oneindex2->second.front());
oneindex2->second.pop_front();
//把oneindex1和oneindex2移位
if (oneindex2->second.size() >= 2)
{
//此时oneindex2肯定不用变
//判断oneindex1是否需要一位
if (oneindex1->second.size() == 0)
oneindex1++;
}
else if (oneindex2->second.size() == 1)
{
if (oneindex1->second.size() == 0)
oneindex1++;
oneindex2++;
}
else if (oneindex2->second.size() == 0)
{
oneindex2++;
if (oneindex2 != ones.rend() && oneindex2->second.size() == 1)
oneindex2++;
while (oneindex1 != ones.rend() && oneindex1->second.size() == 0)
oneindex1++;
}
}
else//选取twoindex
{
maxsum += twoindex->first;
result.push(twoindex->second[0]);
twoindex->second.erase(twoindex->second.begin());
if (twoindex->second.size() == 0)
twoindex++;
}
num -= 2;//容量减2
}
//只加入oneindex1和oneindex2
else if (oneindex2 != ones.rend() && twoindex == twos.rend())
{
LL sum = oneindex1->first + oneindex2->first;
maxsum += sum;
num -= 2;
result.push(oneindex1->second.front());
oneindex1->second.pop_front();
prev[0] = oneindex2->first;
prev[1] = oneindex2->second.front();
result.push(oneindex2->second.front());
oneindex2->second.pop_front();
if (oneindex2->second.size() >= 2)
{
if (oneindex1->second.size() == 0)
oneindex1++;
}
else if (oneindex2->second.size() == 1)
{
if (oneindex1->second.size() == 0)
oneindex1++;
oneindex2++;
}
else if (oneindex2->second.size() == 0)
{
oneindex2++;
if (oneindex2 != ones.rend() && oneindex2->second.size() == 1)
oneindex2++;
while (oneindex1 != ones.rend() && oneindex1->second.size() == 0)
oneindex1++;
}
}
else if (oneindex2 == ones.rend() && twoindex != twos.rend())
{
bool isOneMove = false;
if (oneindex1 != ones.rend())
{
//ones中只余一个,和two进行比较,此时相当于oneindex2为0
if (oneindex1->first > twoindex->first) isOneMove = true;
else if (oneindex1->first < twoindex->first) isOneMove = false;
else if (oneindex1->first == twoindex->first)
{
//比较二者的索引大小
if (oneindex1->second < twoindex->second) isOneMove = true;
else isOneMove = false;
}
}
if (isOneMove)
{
maxsum += oneindex1->first;
num--;
result.push(oneindex1->second.front());
prev[0] = oneindex1->first;
prev[1] = oneindex1->second.front();
oneindex1->second.erase(oneindex1->second.begin());
if (oneindex1->second.size() == 0)
oneindex1++;
}
else
{
maxsum += twoindex->first;
num -= 2;
result.push(twoindex->second.front());
twoindex->second.erase(twoindex->second.begin());
if (twoindex->second.size() == 0)
twoindex++;
}
}
else if (oneindex2 == ones.rend() && twoindex == twos.rend())
{
//只有ones余下一个
while (num > 1 && oneindex1 != ones.rend())
{
maxsum += oneindex1->first;
num--;
result.push(oneindex1->second.front());
prev[0] = oneindex1->first;
prev[1] = oneindex1->second.front();
oneindex1->second.pop_front();
if (oneindex1->second.size() == 0)
oneindex1++;
}
break;
}
}
if (num == 1 && oneindex1 != ones.rend())
{
bool isUndo = false;
if (twoindex != twos.rend())
{
int sum = oneindex1->first + prev[0];
if (sum < twoindex->first) isUndo = true;
else if (sum == twoindex->first)
{
//判断三者的索引大小
if (oneindex1->second[0] > twoindex->second[0]
|| prev[1] > twoindex->second[0])
isUndo = true;
}
}
if (isUndo)
{
if (prev[0] != 0)
{
//剔除oneindex2,选择twoindex
maxsum = maxsum - prev[0] + twoindex->first;
stack<int>temp;
while (result.top() != prev[1])
{
temp.push(result.top());
result.pop();
}
result.pop();
while (!temp.empty())
{
result.push(temp.top());
temp.pop();
}
result.push(twoindex->second[0]);
}
}
else//仍然选择1
{
maxsum += oneindex1->first;
result.push(oneindex1->second[0]);
}
num--;
}
else if (num == 1 && oneindex1 == ones.rend())
{
bool isUndo = false;
if (twoindex != twos.rend())
{
int sum = prev[0];
if (sum < twoindex->first) isUndo = true;
else if (sum == twoindex->first)
{
//判断谁在前面
if (prev[1] > twoindex->second[0])
isUndo = true;
}
}
if (isUndo)
{
if (prev[1] != 0)
{
//剔除oneindex2,选择twoindex
maxsum = maxsum - prev[0] + twoindex->first;
stack<int>temp;
while (result.top() != prev[1])
{
temp.push(result.top());
result.pop();
}
result.pop();
while (!temp.empty())
{
result.push(temp.top());
temp.pop();
}
result.push(twoindex->second[0]);
}
}
else if (oneindex1 != ones.rend())
{
maxsum += oneindex1->first;
result.push(oneindex1->second[0]);
}
num--;
}
if (maxsum > 0)
cout << maxsum << endl;
else
cout << 0 << endl << "No" << endl;
while (!result.empty())
{
cout << result.top() << endl;
result.pop();
}
}
return 0;
}