【数据结构C++】使用有向网络的单源最短路径解决“平分酒”问题

文章描述了一个使用三个无刻度酒瓶将酒分成两个4kg的问题,通过建立状态表和使用深度优先遍历算法寻找解决方案。程序通过输入酒瓶容量和初始/目标状态,生成所有可能的状态并找出所有解法。算法性能分析指出,虽然全局变量减少了空间复杂度,但嵌套调用和递归增加了时间复杂度。
摘要由CSDN通过智能技术生成

要求

由题目已知有三个容量分别为 3kg、5kg和8kg的且没有刻度的酒瓶,3kg和5kg的瓶子均装满了酒,而8kg的瓶子为空。现要求仅用这三个酒瓶将这些酒均分为两个4kg并分别装入5kg和8kg的瓶子中。即图的初始状态为(350),最终状态为(044),本题的求解过程就是把(350)变成(044),也就是找到一条路径,路径的起始点为(350),终点为(044),同时还要输出经过的结点。

步骤

首先初始化各酒瓶的最大容量

    for(i=0;i<N;i++)
	    cin>>full[i];

初始化起点状态

 for(i=0;i<N;i++)
	    cin>>s[0].vexdata[i];//初始状态为s[0]

给出要求的终点

for(i=0;i<N;i++)
	    cin>>final[i];

然后调用jisuan(number)函数,依次求出顶点0的邻接点,求顶点邻接点的过程如下:
通过两个for循环来改变酒瓶的下标和比较,先是i号酒瓶与j号酒瓶比较。
  若i=j,则i和j是同一酒瓶,无需比较和改变。
  若i≠j,则进行下面的操作:

 I=part[i];//保存i酒杯的当前的容量
  J=full[j]-part[j];//j酒杯的空闲量

I是i号酒瓶里当前的酒,J是j号酒瓶里剩余的容量

判断i号酒杯的当前容量是否小于等于j号酒杯的空余量。若是,则将i号酒杯里德酒全部倒入j号酒杯,得到一种新的状态,调用save()函数,保存到状态表。恢复i酒杯和j酒杯改变前的容量;若i号酒杯的当前容量大于j号酒杯的空余量,则将i号酒杯的酒倒入j号酒杯,直至j号酒杯满为止,得到一种新的状态,调用save()函数,保存到状态表。恢复i酒杯和j酒杯改变前的容量。
然后j=j+1,重复上面的步骤,直至j>=N为止;然后是i=i+1,重复上面步骤,直至i>=N为止。这样就把顶点number所有的邻接点都求出来了

源代码

#include<iostream>
using namespace std;
#define N 3           //酒杯数
#define MAX_SIZES 100   //状态表中的最大状态数

 typedef  struct node    //边节点
 {
    int adjvex;//邻接点的位置
    node *next;//指向下一个节点
 }ARCNODE;//结点类型

 typedef struct   //顶点节点
 {
    int vexdata[N];    //各酒杯的存储状态
    ARCNODE *next;//指向下一个邻接点
 }VEXNODE;//表头结点类型

  int A[MAX_SIZES];//用于存储路径的数组
  int top=0;//数组中的个数
  int full[N];//各酒杯的最大容量
  int part[N];//各酒杯的当前容量
  int final[3];//最终状态
  VEXNODE s[MAX_SIZES];  //状态表
  int sta;//状态表当前状态数
  int sum=0;//所有解的个数
  int end_vex;//结束的状态点
  bool visited[MAX_SIZES];//标记状态是否被访问  

  bool compare(int a[],int b[]) //比较状态
  {
    int i;
    for(i=0;i<N;i++)
        if(a[i]!=b[i])
            return true;//两数组不相等,返回true
    return false;//相等,返回false
  }

 void save(int number) //保存状态
 { 
     ARCNODE *p;
     int i;
     for(i=0;i<=sta;i++)
     {
        if(!compare(part,s[i].vexdata))
            break;//状态存在跳出循环
     }
     if(i>sta)//将该状态加入状态表
     {
        sta++;
		for(int j=0;j<N;j++)
			s[sta].vexdata[j]=part[j];
        s[sta].next=NULL;
        if(!compare(part,final))
            end_vex = sta;//该状态是最终状态,记录此位置
     }
     p=new ARCNODE;
     p->adjvex=i;
     p->next=s[number].next;
     s[number].next=p;
  }

void jisuan(int number)//求出某状态的所有邻接点
{
    int i,j,I,J;
    for(i=0;i<N;i++)//编号为i的酒杯与其他酒杯交换
    {
        for(j=0;j<N;j++)
       {
            if(j==i)     continue;//同一酒杯,无法变换
            I=part[i];//保存i酒杯的当前的容量
            J=full[j]-part[j];//j酒杯的空闲量
            if(I<=J)//若i酒杯的当前容量小于等于j酒杯的空闲量
			{
                part[i]=0;
                part[j]+=I;//将i酒杯的酒倒向j酒杯
                save(number);//调用save()函数,保存当前的状态
                part[i]=I;
                part[j]-=I;//恢复各酒杯的原始状态
			}
            else//若i酒杯的当前容量大于j酒杯的空闲量
			{
                part[i]-=J;
                part[j]=full[j];//把j酒杯加满
                save(number);//调用save()函数,保存当前的状态
                part[i]+=J;
                part[j]-=J;//恢复各酒杯的原始状态
			}
        }
    }
}

void create()//建立状态的函数
{
    int number,i;
	cout<<"输入各个酒杯的容积:";
    for(i=0;i<N;i++)
	    cin>>full[i];
	cout<<"输入酒杯的起始状态:";
    for(i=0;i<N;i++)
	    cin>>s[0].vexdata[i];//初始状态为s[0]
	cout<<"输入酒杯的最终状态:";
    for(i=0;i<N;i++)
	    cin>>final[i];//所求的状态
    number=0; 
    sta=0;  
    s[0].next=NULL;
    while(number<=sta)
    {    
		for(i=0;i<N;i++)
			part[i]=s[number].vexdata[i];//将s[number]的状态赋给各酒杯当前的容量
        jisuan(number);//求出该状态所有的邻接点
        number++;
    }
    for(number=0;number<=sta;number++)
         visited[number]=false;
}

void prtf()   //输出路径
{
    int i,j,k;
	cout<<"第"<<sum<<"种解法:" << endl;
    for(i=0;i<top;i++)
    {
        cout << " ";
        j=A[i];//路径中的位置
        for (k = 0; k < N; k++)
            cout << s[j].vexdata[k];//输出该状态	
        cout << " ";
   }
    cout << endl;
}


void dfs(int v0)//深度优先遍历
{
  if(!visited[v0])//判断该状态是否被访问过
    {
        if(v0==end_vex)//判断该状态是否是最终状态
        {
           sum++;
           prtf();//调用输出函数
        }
        else//若不是,深度搜索
        {
            ARCNODE *p;
            p=s[v0].next;
            visited[v0]=true;//标记该状态已访问
            while(p)
            {
              A[top++]=p->adjvex;   //记录路径
              dfs(p->adjvex);//递归调用遍历函数
              A[--top];//清空路径
              p=p->next;
            }
           visited[v0]=false;//标记该状态未访问,用于其它路径中访问
        }
  }
}

void findpath()
{
    A[top++]=0;         //选择深度所搜遍历的起点
    dfs(0);           //进行深度所搜遍历
}

int main()
{   int i,k;
    create();  //建立模型的状态
	cout<<"状态表中的"<< sta + 1<<"种状态:";
    for(i=0;i<=sta;i++)
	   {
        for(k=0;k<N;k++)
			cout<< s[i].vexdata[k];
        cout << " ";
	   }
    findpath(); //输出得到的解
}

运行结果

按照题目要求,首先输入各个酒杯的容积3、5、8,然后输入酒杯的起始状态3、5、0,最后输入酒杯的最终状态0、4、4,按回车键就可以得到全部的16种状态和所有解法,每个解法还展示了从起始态到最终态经过的结点状态。
在这里插入图片描述

算法性能分析

本算法所有问题的求解都是围绕三个酒杯之间的转换进行的,而且函数之间都是嵌套调用,全局变量的使用在一定程度上降低了空间复杂度。三个酒杯的状态在它们之间互相变换,要计算出三个酒杯所有可能的状态,在计算过程中还要用到嵌套调用,再加上循环语句的大量使用,更是增加了时间复杂度。在深度优先遍历算法中用了递归的思想,使算法的时间复杂度进一步增加。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会举重的薯片

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值