阿里的在线编程题中有这么一道题:
一个淘宝的订单中包含n(10>=n>=1)种商品A1,A2,...,An,每种商品数量分别为a1,a2,...,an个,记做{a1,a2,...,an}(ak>0)。订单在仓库生产过程中,仓库为了提升作业效率,会提前对热门组合商品进行预包装。假设这n个商品有m(9>=m>=1)个商品组合,每个组合bomk包含A1,A2,...,An的数量分别为{b1,b2,...,bn}(bk>=0,至少存在一个bk>0)
举例如下:
订单包含A,B,C商品,数量为{2,3,1},商品组合bom1{2,1,1},bom2{1,1,0},bom3{0,1,1}
对以上订单匹配给定商品组合,得到的可能匹配结果为:res1.匹配到组合1一套,剩余B商品;res2.匹配到组合2两套,组合3一套,不剩商品;
现要求订单的最优匹配,最优匹配的原则为:1.匹配组合后,剩余商品种类数越少越好;2.在剩余商品种类数相同的情况下,匹配到的组合种类数越少越好;
例如上面例子,我们认为res2优于res1。
现需要编写程序,输入格式为:
n,m
a1,a2,...,an
bom1,b11,b12,...,b1n
bom2,b21,b22,...,b2n
....
bomm,bm1,bm2,...,bmn
输入数据的格式说明(数据间使用英文逗号分隔):
第一行数据:n个商品,m个预包方案
第二行数据:商品1个数,商品2个数,。。。,商品n个数
第三行数据:bom1,商品1个数,商品2个数,。。。,商品n个数
。。。。
第n行数据:bomn,商品1个数,商品2个数,。。。,商品n个数
针对输入数据找出最优匹配,输出最优匹配的组合及套数。
当时非常遗憾没做出来。最近这段时间空下来了,仔细思考一下,其实并不难。这就是一道典型的可以用搜索算法解决的问题。虽然阿里的开发语言主要是Java,但本人平时还是习惯使用C++,所以这道题就用C++来做了。
下面是我的C++代码。为了方便起见,就不按照在线编程的要求进行多次重复输入验证了,只测试一个用例。
#include <iostream>
#include <vector>
using namespace std;
bool canSearch(const vector<int> &bom, const vector<int> &left,int n) {
//判断能否在剩余商品数量left中再挑选出bom组合
for (int i = 0; i < n; i++) {
if (left[i] - bom[i] < 0) return false;
}
return true;
}
bool allZero(const vector<int> &vec,int m) {
//判断一个元素数量为m 的vector vec的元素是否全为0
for (int i = 0; i < m; i++) {
if (vec[i] != 0) return false;
}
return true;
}
bool isBetter(const vector<int> &left1, const vector<int> &left2,int n,const vector<int> &result1, const vector<int> &result2,int m) {
//在result1和result2两种挑选方法下,剩余商品数量分别为left1和left2
//比较这两种挑选方法哪个更好。如果result1好于retult2,返回true。否则返回false。
int leftsize1=0, leftsize2=0;//剩余的商品种类
for (int i = 0; i < n; i++) {
if (left1[i] > 0) leftsize1++;
if (left2[i] > 0) leftsize2++;
}
if (leftsize1 < leftsize2) return true;
if (leftsize1 > leftsize2) return false;
//剩余商品种类相等的情况下,匹配到的组合种类数越少越好
int pipeisize1 = 0, pipeisize2 = 0;//匹配到的组合种类数
for (int i = 0; i < m; i++) {
if (result1[i] > 0) pipeisize1++;
if (result2[i] > 0) pipeisize2++;
}
if (pipeisize1 <= pipeisize2) return true;
else return false;
}
void search(const vector<int> &maters, const vector<vector<int>> &boms, vector<int> left, vector<int> tmpresult, vector<int> &result, int n, int m)
{
bool flag = false;//能否再添加商品组合
vector<int> tmpleft, tmp_tmpresult;
for (int i = 0; i < m; i++) {
if (canSearch(boms[i], left,n)) {
if (!flag) flag = true;
tmpleft = left;
tmp_tmpresult = tmpresult;
for (int j = 0; j < n; j++) {
tmpleft[j] = left[j] - boms[i][j];
}
tmp_tmpresult[i] += 1;
search(maters, boms, tmpleft, tmp_tmpresult, result, n, m);//还能够继续选出商品组合,递归搜索
}
}
if (!flag) {
//已经没办法再选择一个商品组合,搜索结束,此时得到的一个结果为tmpresult
//将tmpresult与result比较,选一个更优的组合,保存在result中
if (allZero(result, m)) result = tmpresult;//一开始result全0,说明result没被更新过,直接让result=tmpresult即可
else {
//计算在result的挑选方法下,剩余的商品currentbestleft
vector<int> currentbestleft = maters;
for (int i = 0; i < m; i++) {
int number = result[i];
for (int j = 0; j < n; j++) {
currentbestleft[j] -= number*boms[i][j];
}
}
//currentbestleft与left比较
if (isBetter(left,currentbestleft, n, tmpresult, result, m)) {
//现在的结果更好,用现在的结果tmpresult替换之前的结果result
result = tmpresult;
}
}
}
}
int main()
{
int n, m;
cin >> n >> m;
vector<int> maters(n);
vector<vector<int>> boms(m);
cout << "请输入" << n << "种商品的数量:" << endl;
for (int i = 0; i < n; i++) {
cin >> maters[i];
}
int tmp;
cout << "请输入" << m << "种商品组合:" << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> tmp;
boms[i].push_back(tmp);
}
}
vector<int> left = maters;
vector<int> result(m), tmpresult(m);
for (int i = 0; i < m; i++) {
result[i] = 0; tmpresult[i] = 0;
}
search(maters, boms, left, tmpresult, result, n, m);
cout << "最优的匹配结果为:" << endl;
for (int i = 0; i < m; i++) {
cout << result[i] << ' ';
}
cout << endl;
system("pause");
return 0;
}
就用题目中举出来的例子来验证一下程序的正确性吧:
再想一个例子: