参考
https://blog.csdn.net/livelylittlefish/article/details/6555347
假设有280g盐,有一架天平,有两个砝码,分别是4g和14g 。能否在3次内将280g食盐分为100g和180g两堆,请详细描述你的解决方法。
这是另外一种砝码称重问题,类似,但是又不同。砝码称重 这个是给定无限数量砝码,只需计算能不能组成一个重量,不用知道如何组合。砝码称重1是给定砝码个数和重量,以及可以组合的重量,求出任意一种重量是如何组合的。砝码称重2 给定重量和拆分的砝码个数,求拆分后每个砝码的重量。这道题是给定重量和砝码,求如何拆分物体。看似相同,实际上每一个都不一样的。
我们知道4和14可以有这几种组合 0 4 10 14 18,那么问题就是280g物品如何在3次内(说是3次内,基本上就是至少需要3次,我们就按照给定的最大条件来计算,因为如果2次可以,那么3次肯定也可以)用上面的几种砝码组合,拆出100和180.
如果是3次,那么盐肯定是4堆。不过感觉上好像帮不上什么忙。上面参考的作者对问题做了数学分析,可以参考一下。
求解的话就是穷举,分别用这几种砝码组合方式拆分盐,把每次拆分的结果再递归拆分,3次之后得到拆分的所有可能,然后计算有没有符合条件的方案。
这里可以看出,第一次不管如何拆分都是偶数,因为砝码和盐堆都是偶数,怎么组合也没有奇数。
那么第二次呢?可能会出现奇数,比如用14的组合称出14g盐,14g盐平分就是7g。如果出现了奇数,按照题目是拆分成100g和180g是整数,所以就没办法拆分了,因为砝码只有偶数,如果用砝码拆分奇数,得到的会有小数,有了小数,这两堆就没办法继续了,因为再往下,小数会更多,而其他的堆没有小数,所以只能这一堆组合。如果拆分其他堆呢,因为另一堆是偶数,只能平分,也没意义,最后导致与没拆分一样。
如果第二次没有奇数,那么第三次出现奇数也只有平分,那么就不可能出现100g和180g。因为第二次比如得出盐堆a b c,a b c都是偶数,如果把a平分为a1 a2,并且是奇数,那么a1 a2没办法与b c组合,因为b c是偶数,没办法相加得出100g和180g两个偶数,所以只能a1和a2组合,那么就是要求第二次就可以得出100和180,也就是与第二步一样,与没拆分结果一样。
因为砝码和盐堆都是偶数,拆分的也是偶数,出现奇数只能平分,如果一路平分下去,那就要求第一步就直接得出了结果。因为如果只有一个偶数变成奇数,那么只能这两个奇数组合,与没拆分这一步结果一样。如果两个偶数拆分成奇数,那么要么拆分的两堆相互组合,要么内部自己组合,不管怎么组合,都是与没进行这一步结果一样。
所以拆分过程中不可能出现奇数,如果出现,就可以直接跳过了。可以帮我们减少一些计算。
我们看一下上面作者的第一种方法
#define Max_Num 5 using namespace std; int total = 280; //the total weight of the heap of salt int heap1 = 100, heap2 = 180; //the target weight of the two samll heaps of salt int wst[] = { 0, 4, 10, 14, 18 }; //all cases of the weights combination /* the first division result */ int x[Max_Num] = { 0 }, y[Max_Num] = { 0 }; /* the second division result */ int x1[Max_Num][Max_Num] = { {0} }; int x2[Max_Num][Max_Num] = { {0} }; int yy1[Max_Num][Max_Num] = { {0} }; int y2[Max_Num][Max_Num] = { {0} }; /* the third division result */ int x11[Max_Num][Max_Num][Max_Num] = { {{0}} }, x12[Max_Num][Max_Num][Max_Num] = { {{0}} }; int x21[Max_Num][Max_Num][Max_Num] = { {{0}} }, x22[Max_Num][Max_Num][Max_Num] = { {{0}} }; int y11[Max_Num][Max_Num][Max_Num] = { {{0}} }, y12[Max_Num][Max_Num][Max_Num] = { {{0}} }; int y21[Max_Num][Max_Num][Max_Num] = { {{0}} }, y22[Max_Num][Max_Num][Max_Num] = { {{0}} }; /** the first division, according to z=x+y, x <= y */ void weight1() { int z = total; int k = 0, w = 0; for (k = 0; k < Max_Num; k++) { w = wst[k]; x[k] = z / 2 - w; //divide z y[k] = z / 2 + w; if (x[k] % 2 != 0) //no need to judge y[k] x[k] = y[k] = 0; } } void weight2() { int i = 0, k = 0, w = 0; for (i = 0; i < Max_Num; i++) { if (x[i] == 0) //no need to judge y[i] continue; for (k = 0; k < Max_Num; k++) { w = wst[k]; x1[i][k] = (x[i] - w) / 2; //divide x x2[i][k] = (x[i] + w) / 2; if (x1[i][k] % 2 != 0) //no need to judge x2[i][k] x1[i][k] = x2[i][k] = 0; if (x[i] == y[i]) //to avoid repeatance continue; yy1[i][k] = (y[i] - w) / 2; //divide y y2[i][k] = (y[i] + w) / 2; if (yy1[i][k] % 2 != 0) //no need to judge y2[i][k] yy1[i][k] = y2[i][k] = 0; } } } void weight3() { int i = 0, j = 0, k = 0, w = 0; for (i = 0; i < Max_Num; i++) { if (x[i] == 0) //no need to judge y[i] continue; for (j = 0; j < Max_Num; j++) { for (k = 0; k < Max_Num; k++) { w = wst[k]; if (x1[i][j] != 0) //divide x1[i][j] { x11[i][j][k] = (x1[i][j] - w) / 2; x12[i][j][k] = (x1[i][j] + w) / 2; if (x11[i][j][k] % 2 != 0) //x11[i][j][k] and x12[i][j][k] must be even x11[i][j][k] = x12[i][j][k] = 0; } if (x2[i][j] != 0 && x1[i][j] != x2[i][j]) //divide x2[i][j], and to avoid repeatance { x21[i][j][k] = (x2[i][j] - w) / 2; x22[i][j][k] = (x2[i][j] + w) / 2; if (x21[i][j][k] % 2 != 0) //x21[i][j][k] and x22[i][j][k] must be even x21[i][j][k] = x22[i][j][k] = 0; } if (yy1[i][j] != 0) //divide yy1[i][j] { y11[i][j][k] = (yy1[i][j] - w) / 2; y12[i][j][k] = (yy1[i][j] + w) / 2; if (y11[i][j][k] % 2 != 0) //y11[i][j][k] and y12[i][j][k] must be even y11[i][j][k] = y12[i][j][k] = 0; } if (y2[i][j] != 0 && yy1[i][j] != y2[i][j]) //divide y2[i][j], and to avoid repeatance { y21[i][j][k] = (y2[i][j] - w) / 2; y22[i][j][k] = (y2[i][j] + w / 2; if (y21[i][j][k] % 2 != 0) //y21[i][j][k] and y22[i][j][k] must be even y21[i][j][k] = y22[i][j][k] = 0; } } } } } void dump_correct_results() { int i = 0, j = 0, k = 0; for (i = 0; i < Max_Num; i++) { if (x[i] == 0) //no need to judge y[i] continue; for (j = 0; j < Max_Num; j++) { if (x1[i][j] == 0) //no need to judge x2[i][j] continue; for (k = 0; k < Max_Num; k++) { if (x11[i][j][k] != 0 && (x11[i][j][k] + x2[i][j] == heap1 || x12[i][j][k] + x2[i][j] == heap1)) //divide x1[i][j] { printf("%d = %d + %d ", total, x[i], y[i]); printf("%d = %d + %d ", x[i], x1[i][j], x2[i][j]); printf("%d = %d + %d ", x1[i][j], x11[i][j][k], x12[i][j][k]); } } for (k = 0; k < Max_Num; k++) { if (x21[i][j][k] != 0 && (x21[i][j][k] + x1[i][j] == heap1 || x22[i][j][k] + x1[i][j] == heap1)) //divide x2[i][j] { printf("%d = %d + %d ", total, x[i], y[i]); printf("%d = %d + %d ", x[i], x1[i][j], x2[i][j]); printf("%d = %d + %d ", x2[i][j], x21[i][j][k], x22[i][j][k]); } } } for (j = 0; j < Max_Num; j++) { if (yy1[i][j] == 0) //no need to judge y2[i][j] continue; for (k = 0; k < Max_Num; k++) { if (y11[i][j][k] != 0 && (y11[i][j][k] + y2[i][j] == heap1 || y12[i][j][k] + y2[i][j] == heap1)) //divide yy1[i][j] { printf("%d = %d + %d ", total, x[i], y[i]); printf("%d = %d + %d ", y[i], yy1[i][j], y2[i][j]); printf("%d = %d + %d ", yy1[i][j], y11[i][j][k], y12[i][j][k]); } } for (k = 0; k < Max_Num; k++) { if (y21[i][j][k] != 0 && (y21[i][j][k] + yy1[i][j] == heap1 || y22[i][j][k] + yy1[i][j] == heap1)) //divide y2[i][j] { printf("%d = %d + %d ", total, x[i], y[i]); printf("%d = %d + %d ", y[i], yy1[i][j], y2[i][j]); printf("%d = %d + %d ", y2[i][j], y21[i][j][k], y22[i][j][k]); } } } } } struct MyStruct { int weight;//当前是多重的盐 int fm;//通过哪种砝码得到的 MyStruct() { weight = 0; fm = 0; } }; //num 已经拆分了多少次 void cf(int num, int* cfyan) { if (num == 3) { if (cfyan[0] + cfyan[1] == 100 || cfyan[2] + cfyan[3] == 180) { cout << cfyan[0] << "||" << cfyan[1] << "||" << cfyan[2] << "||" << cfyan[3]; } else if (cfyan[0] + cfyan[2] == 100 || cfyan[1] + cfyan[3] == 180) { cout << cfyan[0] << "||" << cfyan[2] << "||" << cfyan[1] << "||" << cfyan[3]; } else if (cfyan[0] + cfyan[3] == 100 || cfyan[1] + cfyan[2] == 180) { cout << cfyan[0] << "||" << cfyan[3] << "||" << cfyan[1] << "||" << cfyan[2]; } } } int main() { int yan = 280; int fm[] = { 0, 4, 10, 14, 18 }; int cfyan[] = { 0, 0, 0, 0 }; { weight1(); weight2(); weight3(); dump_correct_results(); } char inchar; cin >> inchar; }
原作者的代码有个问题,没有覆盖作者第一篇提到的,先用一个砝码称出对应的盐,然后再拆分,作者的逻辑都是平分再去掉对应的砝码重量。
这是上面作者的第二种解法,用链表组成树
#define Max_Num 5 #define To_Be_Devided 2 #define total 280 //the total weight of the heap of salt int wst[] = { 0, 4, 10, 14, 18 }; //all cases of the weights combination int heap1 = 100, heap2 = 180; //the target weight of the two samll heaps of salt struct Division_Node { int parent_be_divided_; //array to_be_divided_[] divided from its parent with this index int step_; //the node step in the division process int heap_[Max_Num - 1]; //all divisions of its parent position for all weights int to_be_divided_[2]; //salt heap index to be divided in next step struct Division_Node *next_[2 * Max_Num]; //all pointers to all child divisions int child_; //the numbe of children (not NULL in array next_) }; //the root, for step 1, heap_[0] will be divided static struct Division_Node root = { 0, 0, {total}, {0}, {NULL}, 0 }; /** the first division, according to z=x+y, x <= y */ void weight1() { int div = 0, k = 0, w = 0; struct Division_Node *cur = &root; int child = 0; int step = 1; for (div = 0; div < To_Be_Devided - 1; div++) //for step 1, only divide heap_[0] { int curpos = cur->to_be_divided_[div]; int z = cur->heap_[curpos]; //the current heap to be divided for (k = 0; k < Max_Num; k++) { w = wst[k]; int x = (z - w) / 2; //divide z int y = (z + w) / 2; if (x % 2 != 0) //no need to judge y[k] continue; //new a node in step 1 struct Division_Node *node1 = (struct Division_Node*)malloc(sizeof(struct Division_Node)); memset(node1, 0, sizeof(struct Division_Node)); node1->parent_be_divided_ = curpos; node1->step_ = step; node1->heap_[curpos] = x; node1->heap_[step] = y; node1->to_be_divided_[0] = curpos; node1->to_be_divided_[1] = step; cur->next_[child++] = node1; //link root and node1 } } cur->child_ = child; } /** the second division, according to x=x1+x2, y=y1+y2, x1<=x2, y1<=y2 but, x and y are saved in node1->to_be_divided_[0] and node1->to_be_divided_[1]. so, unify them to be as x. */ void weight2() { int div = 0, i = 0, k = 0, w = 0; struct Division_Node *cur = &root; int step = 2; for (i = 0; i < cur->child_; i++) //for each node1 in step1 { struct Division_Node *node1 = cur->next_[i]; //step 2 will use all nodes created in step 1 //to avoid repeatance int to_be_divided = To_Be_Devided; if (node1->heap_[node1->to_be_divided_[0]] == node1->heap_[node1->to_be_divided_[1]]) to_be_divided--; int child = 0; for (div = 0; div < to_be_divided; div++) //for step 2, will divide x=heap_[0], y=heap_[1] of node1 { int curpos = node1->to_be_divided_[div]; int x = node1->heap_[curpos]; //the current heap to be divided for (k = 0; k < Max_Num; k++) { w = wst[k]; int x1 = (x - w) / 2; //divide x or y, use x in order to be in a uniform int x2 = (x + w) / 2; if (x1 % 2 != 0) //no need to judge x2 continue; //new a node in step 2 struct Division_Node *node2 = (struct Division_Node*)malloc(sizeof(struct Division_Node)); memset(node2, 0, sizeof(struct Division_Node)); memcpy(node2->heap_, node1->heap_, (Max_Num - 1) * sizeof(int)); //copy from its parent node2->parent_be_divided_ = curpos; node2->step_ = step; node2->heap_[curpos] = x1; node2->heap_[step] = x2; node2->to_be_divided_[0] = curpos; node2->to_be_divided_[1] = step; node1->next_[child++] = node2; //link current node1 and node2 } } node1->child_ = child; } } /** the third division, according to x1=x11+x12, y1=y11+y12, x11<=x12, y11<=y12 x2=x21+x22, y2=y21+y22, x21<=x22, y21<=y22 but, x1 or x2 or y1 or y2 is saved in heap_ of each node2 with index node2->to_be_divided_[0] and node2->to_be_divided_[1]. so, unify them to be as x1. */ void weight3() { int div = 0, i = 0, j = 0, k = 0, w = 0; struct Division_Node *cur = &root; int step = 3; for (i = 0; i < cur->child_; i++) //for each node1 in step1 { struct Division_Node *node1 = cur->next_[i]; for (j = 0; j < node1->child_; j++) //for each node2 of node1 in step2 { struct Division_Node *node2 = node1->next_[j]; //to avoid repeatance int to_be_divided = To_Be_Devided; if (node2->heap_[node2->to_be_divided_[0]] == node2->heap_[node2->to_be_divided_[1]]) to_be_divided--; int child = 0; for (div = 0; div < to_be_divided; div++) //for step 3, will divide x1=heap_[0], x2=heap_[2] of node2 { int curpos = node2->to_be_divided_[div]; int x1 = node2->heap_[curpos]; //the current heap to be divided, x1, or x2, or y1, or y2 for (k = 0; k < Max_Num; k++) { w = wst[k]; int x11 = (x1 - w) / 2; //divide x or y, use x in order to be in a uniform int x12 = (x1 + w) / 2; if (x11 % 2 != 0) //no need to judge x12 continue; //new a node in step 3 struct Division_Node *node3 = (struct Division_Node*)malloc(sizeof(struct Division_Node)); memset(node3, 0, sizeof(struct Division_Node)); memcpy(node3->heap_, node2->heap_, (Max_Num - 1) * sizeof(int)); //copy from its parent node3->parent_be_divided_ = curpos; node3->step_ = step; node3->heap_[curpos] = x11; node3->heap_[step] = x12; node3->to_be_divided_[0] = curpos; //in fact, this array in step3 needed for dump node3->to_be_divided_[1] = step; node2->next_[child++] = node3; //link current node2 and node3 } } node2->child_ = child; } } } void dump_correct_results(struct Division_Node* node) { int i = 0, j = 0, k = 0; struct Division_Node *cur = node; for (i = 0; i < cur->child_; i++) //for each node1 in step1 { struct Division_Node *node1 = cur->next_[i]; for (j = 0; j < node1->child_; j++) //for each node2 of node1 in step2 { struct Division_Node *node2 = node1->next_[j]; for (k = 0; k < node2->child_; k++) //for each node3 of node2 in step 3 { struct Division_Node *node3 = node2->next_[k]; /** unify them into the following equations z = x + y, x <= y x = x1 + x2, x1 <= x2 x1 = x11 + x12, x11 <= x12 */ int x11 = node3->heap_[node3->to_be_divided_[0]]; int x12 = node3->heap_[node3->to_be_divided_[1]]; int x2index = 0; if (node3->parent_be_divided_ == node2->to_be_divided_[0]) x2index = node2->to_be_divided_[1]; else x2index = node2->to_be_divided_[0]; int x2 = node2->heap_[x2index]; if (x11 + x2 == heap1 || x12 + x2 == heap1) { printf("%d = %d + %d ", total, node1->heap_[node1->to_be_divided_[0]], node1->heap_[node1->to_be_divided_[1]]); printf("%d = %d + %d ", node1->heap_[node2->parent_be_divided_], node2->heap_[node2->to_be_divided_[0]], node2->heap_[node2->to_be_divided_[1]]); printf("%d = %d + %d ", node2->heap_[node3->parent_be_divided_], x11, x12); } } } } } void free_all_nodes(struct Division_Node* node) { int i = 0, j = 0, k = 0; struct Division_Node *cur = node; for (i = 0; i < cur->child_; i++) //for each node1 in step1 { struct Division_Node *node1 = cur->next_[i]; for (j = 0; j < node1->child_; j++) //for each node2 of node1 in step2 { struct Division_Node *node2 = node1->next_[j]; for (k = 0; k < node2->child_; k++) //for each node3 of node2 in step 3 { struct Division_Node *node3 = node2->next_[k]; free(node3); } free(node2); } free(node1); } } int main(/* int argc, char** argv */) { weight1(); weight2(); weight3(); dump_correct_results(&root); free_all_nodes(&root); char inchar; cin >> inchar; }
我做了改进,增加了单独称出一些盐的条件,不足之处,就是有重复数据,没有判断。
int fmweight[] = { 0, 4, 10, 14, 18 }; struct MNODE { vector<int> mweight;//当前拆分的盐堆 vector<MNODE*> mnext;//当前盐堆对应的拆分 }; void devsalt(MNODE* pnode) { if (pnode->mweight.size() == 4) { if (pnode->mweight[0] + pnode->mweight[1] == 100) { cout << pnode->mweight[0] << " " << pnode->mweight[1] << "||" << pnode->mweight[2] << " " << pnode->mweight[3] << endl; } else if (pnode->mweight[0] + pnode->mweight[2] == 100) { cout << pnode->mweight[0] << " " << pnode->mweight[2] << "||" << pnode->mweight[1] << " " << pnode->mweight[3] << endl; } else if (pnode->mweight[0] + pnode->mweight[3] == 100) { cout << pnode->mweight[0] << " " << pnode->mweight[3] << "||" << pnode->mweight[1] << " " << pnode->mweight[2] << endl; } else if (pnode->mweight[1] + pnode->mweight[2] == 100) { cout << pnode->mweight[1] << " " << pnode->mweight[2] << "||" << pnode->mweight[0] << " " << pnode->mweight[3] << endl; } else if (pnode->mweight[1] + pnode->mweight[3] == 100) { cout << pnode->mweight[1] << " " << pnode->mweight[3] << "||" << pnode->mweight[0] << " " << pnode->mweight[2] << endl; } else if (pnode->mweight[2] + pnode->mweight[3] == 100) { cout << pnode->mweight[2] << " " << pnode->mweight[3] << "||" << pnode->mweight[0] << " " << pnode->mweight[1] << endl; } } else { for (int i = 0; i < pnode->mweight.size(); i++) { for (int j = 0; j < 5; j++) { { int x = (pnode->mweight[i] - fmweight[j]) / 2; int y = (pnode->mweight[i] + fmweight[j]) / 2; if (x % 2 == 0) { MNODE* tnode = new MNODE; for (int p = 0; p < pnode->mweight.size(); p++) { if (p != i) { tnode->mweight.emplace_back(pnode->mweight[p]); } } tnode->mweight.emplace_back(x); tnode->mweight.emplace_back(y); pnode->mnext.emplace_back(tnode); devsalt(tnode); } } if(fmweight[j] > 0) { int x = pnode->mweight[i] - fmweight[j]; int y = fmweight[j]; if (x > 0 && x % 2 == 0) { MNODE* tnode = new MNODE; for (int p = 0; p < pnode->mweight.size(); p++) { if (p != i) { tnode->mweight.emplace_back(pnode->mweight[p]); } } tnode->mweight.emplace_back(x); tnode->mweight.emplace_back(y); pnode->mnext.emplace_back(tnode); devsalt(tnode); } } } } } } int main() { MNODE* phead = new MNODE; phead->mweight.emplace_back(280); devsalt(phead); char inchar; cin >> inchar; }
虽然增加了单独称出某些盐的方法,但是并没有增加结果,因为如果单独称出某些盐,这道题只能是利用已知的盐堆,这样的话,情况更多,这里就没有处理。别忘了释放内存。