BFS解决一般性的泊松分酒问题
有一只装满8斤酒的瓶子和两只分别装5斤和3斤酒的空瓶,如何才能将这8斤酒分为两等份。这个问题规模小,手工就可以完成。那么更大规模和一般性的问题又如何解决呢?
通过广度优先搜索(BFS)对状态空间进行逐步搜索,若能找到答案,则为最少步数解,反之则无解。具体过程如下:
设共有n个瓶子,最大容量分别为(x1,x2,...,xn),则共有(x1+1)(x2+1)...(xn+1)种可能的状态,搜索空间过于庞大。但考虑到瓶子的容量差,在倒酒的过程中不可能出现所有的情况。因此要对新加入图中的状态进行判断,即剪枝,如果新生成的状态是可行的,将其加入图中,反之将其舍弃。在广度优先搜索的过程中,使用一个队列Q存储新生成的状态,一个数组Visited存储出现过的状态。搜索过程如下所示
Step1. 将初始状态(x10,x20,...,xn0)加入到队列Q以及Visited数组中。
Step2. 将队列Q队头的状态出队列,找出Q可能生成的状态。
Step3. 判断新生成的状态是否为目标状态,若是,则停止搜索,若不是,继续执行Step4。
Step4. 判断新生成的状态是否位于Visited数组中,若已存在则跳过该状态判断下一状态。若未存在则将其加入到队列Q尾部,并加入到Visited数组中,并判断下一个状态。
Step4. 不断执行Step2,Step3和Step4,直到队列为空,停止程序,此时未找到可行的解,则该问题无解。
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#define TRUE 1
#define FASLE 0
#define MAXSIZE 100000
typedef struct{
int **rear,**front;
int Queuelen;
}Queue;
void InitQueue(Queue &Q, int n){
Q.rear = Q.front = (int**)malloc(sizeof(int*) * MAXSIZE);
for(int i = 0; i < MAXSIZE; i++){
Q.rear[i] = Q.front[i] = (int*)malloc(sizeof(int) * n);
}
Q.Queuelen = 0;
}
void EnQueue(Queue &Q,int* e){
*(Q.rear++) = e;
}
void DeQueue(Queue &Q,int* &e){
e = *(Q.front++);
}
int QueueEmpty(Queue Q){
if(Q.front == Q.rear)
return TRUE;
else
return FASLE;
}
void pour(const int* status, const int* volume, int i, int j,int* new_volume, int n){
for(int k = 0; k < n; k++){
new_volume[k] = status[k];
}
if(new_volume[i] + new_volume[j] <= volume[j]){
new_volume[j] += new_volume[i];
new_volume[i] = 0;
}
else{
new_volume[i] -= (volume[j] - new_volume[j]);
new_volume[j] = volume[j];
}
}
int exist(int** visited, int* new_volume,int n, int num){
int flag;
for(int i = 0; i < num; i++){
flag = 1;
for(int j = 0; j < n; j++){
if(visited[i][j] != new_volume[j]){
flag = 0;
break;
}
}
if(flag) break;
}
return flag;
}
void append(int** &visited, int &num, int* new_volume, int n){
for(int i = 0; i < n; i++){
visited[num][i] = new_volume[i];
}
num++;
}
int achieve(const int* target, const int* new_volume, int n){
int flag = 1;
for(int i = 0; i < n; i++){
if(target[i] != new_volume[i]){
flag = 0;
break;
}
}
return flag;
}
void print(const int* current_volume, int* new_volume, int n){
for(int i = 0; i < n - 1; i++){
printf("%d,",current_volume[i]);
}
printf("%d",current_volume[n - 1]);
printf("---");
for(int i = 0; i < n - 1; i++){
printf("%d,",new_volume[i]);
}
printf("%d\n",new_volume[n - 1]);
}
void copy_volume(const int* new_volume, int* copy, int n){
for(int k = 0; k < n; k++){
copy[k] = new_volume[k];
}
}
void BFS(int* volume, int* current_volume, int n, int* target){
Queue Q;
InitQueue(Q,n);
EnQueue(Q, current_volume);
int num = 0;
int** visited;
visited = (int**)malloc(sizeof(int*) * MAXSIZE);
for(int i = 0; i < MAXSIZE; i++){
visited[i] = (int*)malloc(sizeof(int) * n);
}
append(visited, num, current_volume, n);
int* status = (int*)malloc(sizeof(int) * n);
int* new_volume = (int*)malloc(sizeof(int) * n);
int layer = 0;
int* copy;
int qlen[MAXSIZE];
qlen[layer] = 1;
int achieved = 0;
while(!QueueEmpty(Q)){
DeQueue(Q, status);
for(int i = 0; i < n; i++){
if(status[i] != 0){
for(int j = 0; j < n; j++){
if(j == i){
//j--;
continue;
}
if(status[j] == volume[j]) continue;
pour(status, volume, i, j, new_volume, n);
copy = (int*)malloc(sizeof(int) * n);
copy_volume(new_volume, copy, n);
if(exist(visited, copy, n, num)) continue;
else{
print(status, copy, n);
if(achieve(target, new_volume, n)){
printf("已找到答案! ");
printf("当前第%d层--------------------------\n",layer + 1);
system("pause");
achieved = 1;
}
append(visited, num, copy, n);
EnQueue(Q, copy);
}
}
}
}
qlen[layer]--;
if(!qlen[layer]){
printf("第%d层遍历完毕--------------------------\n",layer + 1);
layer++;
qlen[layer] = Q.rear - Q.front;
}
}
if(!achieved) printf("未找到答案! ");
printf("搜索完毕\n***************************************\n\n");
}
int main(){
int n;
while(1){
printf("请输入瓶子个数\n");
scanf("%d",&n);
int* volume;
int* current_volume;
int* target;
target = (int*)malloc(sizeof(int) * n);
volume = (int*)malloc(sizeof(int) * n);
current_volume = (int*)malloc(sizeof(int) * n);
int step = 0;
printf("请输入各个瓶子的最大容量\n");
for(int i = 0; i < n; i++){
scanf("%d",&volume[i]);
}
printf("请输入各个瓶子的当前容量\n");
for(int i = 0; i < n; i++){
scanf("%d",¤t_volume[i]);
}
printf("请输入各个瓶子的目标容量\n");
for(int i = 0; i < n; i++){
scanf("%d",&target[i]);
}
BFS(volume, current_volume, n, target);
}
return 0;
}
/*
3
8 5 3
8 0 0
4 4 0
*/