题目来源:赛码网-生日礼物(京东2016实习生真题)
题目描述
BF的生日快到了,这一次,小东决定为BF送一份特别的生日礼物为其庆生。作为高智商中的佼佼者,BF在国外求学,因此小东无法与之一起庆生。小东计划送一个生日卡片,并通过特别的包装让BF永远难忘。
她决定把卡片套装在一系列的信封A = {a1, a2, …, an}中。小东已经从商店中购买了很多的信封,她希望能够用手头中尽可能多的信封包装卡片。为防止卡片或信封被损坏,只有长宽较小的信封能够装入大些的信封,同尺寸的信封不能套装,卡片和信封都不能折叠。
输入 输入有若干组,每组的第一行包含三个整数n, w, h,1<=n<=5000, 1<=w, h<=10^6,分别表示小东手头的信封数量和卡片的大小。紧随其后的n行中,每行有两个整数wi和hi,为第i个信封的大小,1<=wi, hi<=10^6。
输出 对每组测试数据,结果第一行中输出最多能够使用的信封数量,结果第二行中按使用顺序输出信封的编号。由于小东有洁癖,她对排在前面的信封比较有好感,若有多个信封可用,她喜欢用最先拿到的信封。另外别忘了,小东要求把卡片装入能够装的最小信封中。 如果卡片无法装入任何信封中,则在单独的行中输出0。
思路
我看题时对题意有两个疑惑,一是w和h的值可否交换,二是信封的大小判断包不包括(w1==w2 && h1<h2) || (w1<w2 && h1==h2)这种情况。第一个问题通过测试数据发现是不允许交换的,第二个问题其实题目中已经说明“只有长宽较小的信封能够装入大些的信封”,所以这种情况也是不允许的。
我整体的思路是使用动态规划完成的,首先存储了尺寸超过卡片的所有信封作为备选,选用map结构存储,使所有信封依次按照w、h从小到大排序。依序遍历每一个备选信封,以当前信封a作为最后一个信封,遍历尺寸比它小的所有信封(因为前面所有的值都会影响当前的结果,所以都需要遍历),计算出此时能够套装的信封总数的最大值,保存最大值的情况下a的前一个信封的索引号。
编程问题记录
编程调试时出现了堆损坏的错误,问题出在两个地方,
初始化maxnum和previd数组时数组大小错写成了map的大小,应该写成所有信封的数目,因为索引 值对应的初始输入的数目,而非map的数目;把new int[]写成了new int(),造成堆损坏的错误,这个习惯要改正!注意重载<和==时函数规范,函数参数是const &,否则map中使用find查询时结果是错误的。 bool operator < (const size& rhs) const …… bool operator == (const size& rhs) const ……
#include<iostream> #include<map> #include<stack> using namespace std; struct size { int w; int h; bool operator < (const size& rhs) const { return (w < rhs.w) || (w == rhs.w && h<rhs.h); } bool operator == (const size& rhs) const { return w == rhs.w && h == rhs.h; } }; int main() { int num, w, h; stack<int>linkid; while (scanf("%d%d%d", &num, &w, &h) != EOF) { //无效代码 //交换w,h //if (w > h) //{ // int temp = w; w = h; h = temp; //} map<size, int>select; for (int i = 1; i <= num; i++) { int wi, hi; cin >> wi >> hi; //无效代码 //if (wi > hi) //{ // int temp = wi; wi = hi; hi = temp; //} if (wi>w && hi>h) { size env = { wi, hi }; if (select.find(env) == select.end()) select.insert(pair<size, int>(env, i)); } } int packnum = select.size(); if (packnum > 0) { map<size, int>::iterator it = select.begin(); map<size, int>::iterator pit; int maxid = it->second; int *maxnum = new int[num + 1]; maxnum[maxid] = 1; int *previd = new int[num + 1]; previd[maxid] = -1; it++; for (; it != select.end(); it++) { //寻找前一个点 int cnt = 0, pid = -1; for (pit = select.begin(); pit != it; pit++) { if (pit->first.w < it->first.w && pit->first.h < it->first.h) { int curnum = maxnum[pit->second] + 1; if ((curnum>cnt) || (curnum == cnt && pit->second < pid)) { cnt = curnum; pid = pit->second; } } } if (cnt == 0)cnt = 1; maxnum[it->second] = cnt; previd[it->second] = pid; if ((cnt>maxnum[maxid]) || (cnt == maxnum[maxid] && it->second < maxid)) maxid = it->second; } select.clear(); int id = maxid; int count = 0; while (id >= 0) { linkid.push(id); count++; id = previd[id]; } cout << count << endl; delete[] previd; delete[] maxnum; previd = NULL; maxnum = NULL; if (count > 0) { while (!linkid.empty()) { int curid = linkid.top(); linkid.pop(); cout << curid << " "; } cout << endl; } } else { cout << 0 << endl; } } return 0; }