水桶分水问题详解(C++实现)
Posted 发呆哥o_o ....
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了水桶分水问题详解(C++实现)相关的知识,希望对你有一定的参考价值。
发呆——尽量用最简单的话语让读者理解问题思路,让读者不在发呆
Talk is cheap, show me the code .
你知道的越多,你不知道的越多。
两水桶分水
如果你有无穷多的水,一个3 公升的提捅,一个5 公升的提捅,两只提捅形状上下都不均 匀,如何才能准确称出4 公升的水?
按照原始逻辑,两个水桶不断地分水,倒水
大桶向小桶倒满水,然后小桶满了,把小桶的水倒了,大桶的水倒向小桶
小桶满了以后,继续小桶的水倒了,大桶的倒向小桶
或者大桶倒小桶的时候,大桶没水了,把大桶接满水,
继续这种循环
小桶水量为i 大桶水量为j
j->i 大桶向小桶倒水,小桶满了
i=0 小桶倒空
j->i 大桶继续向小桶倒水,小桶满了继续
如果出现大桶为空,就把大桶接满水,继续这种循环
简单来说两桶分水的规律就是:
大桶没水了,就把大桶接满水
大桶有水,就往小桶倒水
小桶满了,就把小桶倒了,重新让大桶向小桶倒水
如果还不能理解我们画一个矩阵描述
这里我们以3L和5L的桶,求得目标为4L的水量为例子
横向是大桶的水量
竖向是小桶的水量
刚开始两个桶都没有水,按照规律我们向大桶填满水
大桶向小桶填水
小桶填满了,把小桶倒空
小桶倒空了,大桶继续向小桶倒
大桶没水了,小桶未满,把大桶水接满
大桶有水了,继续向小桶倒水
把小桶填满了,把小桶倒空
此时我们得到了目标值,大桶里为4L
能得到的判断就是小桶里没水,大桶里有目标水量
起始点就是0,0
如果我们在这个过程中能走到重复的位置,还没有找到目标值,证明不能凑出目标水量
也就是说,如果过程中还能走到0,0点,我们之间退出就行,有重复路径了,不能凑出目标水量
#include <iostream>
using namespace std;
void printStates(int x, int y){ //输入一下装水的过程
cout << "小水桶容量为:" << x << "," << "大水桶容量为:" << y << "\\n";
}
bool twoBucket(int x, int y, int target){ //x是小桶的容量,y是大桶的容量,target是要求的目标水量
int i = 0, j = 0; //i是小桶里的水,j是大桶里的水
while (true) {
if (i == 0 || j == y) { //当小桶没水或者大桶满水->大桶向小桶倒水
while (i != x && j > 0) { //倒水的时候一定要保证大桶里有水
i++;j--;
printStates(i, j);
}
}
if (i == x) { //小桶水满了,就把小桶的水倒空了
while (i != 0) {
i--;
printStates(i, j);
}
if (i == 0 && j == target) { //如果此时大桶的水符合目标水量,直接跳出
return true;
}
if (i == 0 && j == 0) { //如果重复到两桶都没水了,证明没有办法得到目标值
return false;
}
}
if (j == 0) { //如果大桶没水了,就把大桶加满水
while (j != y){
j++;
printStates(i, j);
}
}
}
}
int main(){
cout << "请输入两个水桶的容量,以及想获得的目标水量";
int x,y,target;
cin >> x >> y >> target; //输入两个水桶的容量
int temp = x < y ? x : y; //把小容量的给x,大容量的给y
y = x < y ? y : x;
x = temp;
cout << (twoBucket(x, y, target) ? "可以凑成" : "无法凑成");
return 0;
}
直接的输出结果,把输出语句放到当前倒水while循环的外面
符合上述规律
三水桶分水
题目要求:有一个容积为8升的水桶里装满了水,另外还有一个容积为3升的空桶和一个容积为5升的空桶,
如何利用这两个空桶等分8升水?附加条件是三个水桶都没有体积刻度,也不能使用其它辅助容器。
这个问题其实和上面的也差不多,但这里有一个特点就是只有8L水
该图就是三个桶,容量分别为8L,5L,3L 其中8L的水是满的
根据图来看,大桶向中桶倒水,然后中桶向小桶倒水
上面两桶分水的规律就是:
大桶没水了,就把大桶接满水
大桶有水,就往小桶倒水
小桶满了,就把小桶倒了,重新让大桶向小桶倒水
其实这里也是大概的想法,
小桶不满,就把中桶的水倒向小桶
中桶没水了,就把大桶的水向里面倒
小桶满了,就把小桶的倒向大桶(这里是只有8L水,并不是无限水)
这里我们用个bool数组来判断是不是走过当前点
有的时候三个桶会陷入循环圈,不能再用上面两桶的判断是否走过起始点了
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void printStates(int x, int y, int z){ //输入一下装水的过程
cout << "小水桶容量为:" << x << "," << "中水桶容量为:" << y << "," << "大水桶容量为:" << z << "\\n";
}
bool twoBucket(int x, int y, int z, int targeta, int targetb){ //x是小桶的容量,y是中桶的容量,z是大桶的容量
bool isbool[x + 1][y + 1][z + 1]; //建立数组记录是否走过当前位置
for (int i = 0; i <= x; i++) { //isbool[i][j][k]代表小水桶为i,中为j,大为k是否以前出现过,
for (int j = 0; j <= y; j++) { // 如果出现过以前的步骤说明不能获得目标值
for (int k = 0; k <= z; k++) { //先初始化数组,每个位置都没走过
isbool[i][j][k] = false;
}
}
}
int i = 0, j = 0, k = z; //从起始位置开始走
while (true) {
if (isbool[i][j][k]) { //如果走过,直接返回false
return false;
}
isbool[i][j][k] = true; //没走过就标记当前位置
//如果三个桶中有两个能获得目标值了,证明完成分水任务,返回true
if (i == targeta && j == targetb || j == targeta && k == targetb || i == targeta && k == targetb) {
return true;
}
else if (i == x) { //小桶水满了,向大桶倒水
while (i > 0) {
i--;
k++;
}
printStates(i, j, k);
}
else if (i == 0 && j == 0) { //小桶大桶都空着,大桶向中桶倒水
while (j < y && k > 0) {
j++;
k--;
}
printStates(i, j, k);
}
else if (i < x && j != 0) { //小桶没满,中桶不空,中桶向小桶倒水
while (i < x && j > 0) {
i++;
j--;
}
printStates(i, j, k);
}
else if (i != 0 && j == 0) { //小桶不空,中桶空着,大桶向中桶倒水
while (k > 0 && j < y ) {
k--;
j++;
}
printStates(i, j, k);
}
}
}
int main(){
cout << "请输入三个水桶的容量,以及想要分成的两个水量,默认大水桶是装满水的\\n";
int x,y,z, targeta, targetb;
cin >> x >> y >> z >> targeta >> targetb; //输入三个水桶的容量,以及想要分成的两个水量
int num[3] = {x, y, z}; //把小容量的给x,中容量的给y,大容量的给z
sort(num,num + 3); //从num[0]到num[3]之前的进行排序(num[3]不参与排序)
cout << (twoBucket(num[0], num[1], num[2], targeta, targetb) ? "可以平分" : "无法平分");
return 0;
}
以上是关于水桶分水问题详解(C++实现)的主要内容,如果未能解决你的问题,请参考以下文章
[年薪60W分水岭]基于Netty手写实现Apache Dubbo(带注册中心和注解)
我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情