算法分析与设计-实验四 回溯算法设计


一、实验目的:
掌握用回溯法解题的算法框架;根据回溯法解决实际问题。

二、实验所用仪器及环境
Windows 7 以上操作系统,PC机,codeblocks环境

三、实验原理:
算法总体思想:回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。这种方法适用于解一些组合数相当大的问题。回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
(1)问题的解向量:回溯法希望一个问题的解能够表示成一个n元式(x1,x2,…,xn)的形式。
(2)显约束:对分量xi的取值限定。
(3)隐约束:为满足问题的解而对不同分量之间施加的约束。
(4)解空间:对于问题的一个实例,解向量满足显式约束条件的所有多元组,构成了该实例的一个解空间。
基本步骤:
(1)针对所给问题,定义问题的解空间,主要有子集树(如图1所示)和排列树(如图2所示)两种解空间形式。
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

图1 子集树
在这里插入图片描述

图2 排列树
在这里插入图片描述

四、实验内容:

1、0-1背包问题

有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。要求每个物品要么放进背包,要么不放进背包。

#include <iostream>
#include<bits/stdc++.h>
using namespace std;

#define maxn 100000

int w[maxn]={0};
int v[maxn]={0};
int ans[maxn]={0};
int a[maxn]={0};
int n;
int Maxw,Sumv;


int huisu(int t,int Sw,int Sv)
{
    if(t>=n)///最大层了
    {
        if(Sumv<Sv)///永远取最大值
        {
            Sumv=Sv;
            for(int i=0;i<n;i++)
            {
                ans[i]=a[i];
            }
        }
    }else{
        for(int i=0;i<=1;i++)//0-1
        {
            a[t]=i;///临时选择方式
            Sw=Sw+i*w[t];
            Sv=Sv+i*v[t];
            if(Sw<=Maxw)///目前的实际重量比最大重量(重量上限)小才行
            {
                huisu(t+1,Sw,Sv);
            }else{
                Sw-=i*w[t];///拿出来
                Sv-=i*v[t];

            }

        }

    }



}

int main()
{


    cin>>n>>Maxw;

    for(int i=0;i<n;i++)
    {
        cin>>v[i];
    }
    for(int i=0;i<n;i++)
    {
        cin>>w[i];
    }
    huisu(0,0,0);///0号层,重量0,价值0
    for(int i=0;i<n;i++)
    {
        cout<<ans[i]<<" ";/// 最优选择
    }
    cout<<endl;
    cout<<Sumv;







    return 0;
}

//void backtrack(int k)
//{
//    if(到达边界) 更新或者输出结果
//    else
//    {
//        对于每一种可能进行操作(for循环 i)
//        {
//            if(限定条件合法)
//                backtrack(k+i);
//        }
//    }
//
//}

//n 代表的是物品个数
//
//MaxWight 背包能装的最大体积
//
//SumValue 物品的最大价值
//
//ans 数组,最终选择方式
//
//a 数组,临时选择方式
//
//
//
//思路:物品只有两种选择,放或者不放,构造二叉树,n 个物品就是 n 层树,然后进行最优值更新。

//in
//4 10
//1 3 5 9
//2 3 4 7
//out
//0 1 0 1
//12

2、旅行售货员问题

设有一个售货员从城市1出发,到城市2,3,…,n去推销货物,最后回到城市1。假定任意两个城市i,j间的距离dij(dij=dji)是已知的,问他应沿着什么样的路线走,才能使走过的路线最短。

#include <iostream>
#include<bits/stdc++.h>
using namespace std;

#define maxn 10000

int n;
int dis[maxn][maxn]={0};
int mindis=maxn;
int x[maxn];
int bestx[maxn];
int cw;

///0-  n-1 城
int BackTrack(int t)
{

    if(t>=n)
    {
        for(int i=1;i<=n;i++)
        {
            bestx[i]=x[i];
        }
        mindis=cw+dis[x[n-1]][x[n]]+dis[x[n]][1];///最优值走到头才更新
        return mindis;
    }else{

        for(int i=t;i<=n;i++)
        {
            if(cw+dis[x[t-1]][i]<mindis||mindis==maxn)
            {
                swap(x[t],x[i]);///遍历全排列
                cw+=dis[x[t-1]][x[t]];
                BackTrack(t+1);
                cw-=dis[x[t-1]][x[t]];
                swap(x[t],x[i]);
            }

        }
    }


}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>dis[i][j];
    for(int i=1;i<=n;i++)
    {
        x[i]=i;
    }
    BackTrack(2);
    cout<<mindis;

    return 0;
}

//void backtrack(int k)
//{
//    if(到达边界) 更新或者输出结果
//    else
//    {
//        对于每一种可能进行操作(for循环 i)
//        {
//            if(限定条件合法)
//                backtrack(k+i);
//        }
//    }
//
//}
//#回溯法#回溯法+排序树为什么swap?https://blog.csdn.net/qq_37967797/article/details/92624531?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-1-92624531.nonecase&utm_term=%E5%9B%9E%E6%BA%AF%E6%B3%95%E6%8E%92%E5%88%97%E6%A0%91swap%E4%BD%9C%E7%94%A8&spm=1000.2123.3001.4430
//4
//-1 30 6 4
//30 -1 5 10
//6 5 -1 20
//4 10 20 -1
//25

3、图的m着色问题

(1)问题描述:给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色。若这个图不是m可着色的,就输出no。若是则输出每种着色方案和可行方案数。
(2)样例1
输入:当输入如下图时

顶点数:5
颜色数 4
边数:9
各边顶点:
1 2
1 3
1 4
2 3
2 4
2 5
3 4
3 5
4 5
输出:

样例2:

(3)实验步骤
(a)首先将给定的图利用抽象图表示出来
(b)判断该节点k当前的着色是否符合条件,需要判断x[k]与k结点其他相邻节点h的x[h]是否相等
(c)回溯过程,若此时的结点值已经大于结点总数,代表已经着色完成,并且找到了一种可行解,此时可以将可行解数+1
(d)回溯从最后一个结点往上回溯,并一层一层更改结点至其他可用着色,以此来找到所有的填色方案。

#include <iostream>
#include<bits/stdc++.h>
using namespace std;

#define maxn 10000

int n,m,sum;
int w[maxn][maxn]={0};
int bestw=maxn;
int x[maxn];///全排列的缓冲?
int bestx[maxn];
int cw;
int tong(int k){
    for(int i=1;i<k;i++)
    {
        if(w[k][i]==1&&x[i]==x[k])
        {

            return 0;
        }
    }
    return 1;
}

void  BackTrack(int t)
{
    if(t>n)
    {
        for(int i=1;i<=n;i++)
        {
            cout<<x[i]<<" ";
        }
        cout<<endl;
        sum++;

    }else{
        for(int i=1;i<=m;i++)
        {
            x[t]=i;

            if(tong(t)==1)
            {
                //cout<<"*";
                BackTrack(t+1);
            }
            x[t]=0;
        }


    }

}

int main()
{
    cin>>n;
int bian;
    cin>>m;
    cin>>bian;
//    for(int i=0;i<=n;i++)
//        for(int j=0;j<=n;j++)
//            w[i][j]=-1;
    for(int i=1;i<=bian;i++)
    {
        int x,y;
        cin>>x>>y;
        w[x][y]=1;
        w[y][x]=1;
    }

//    for(int i=0;i<=n;i++)
//        {for(int j=0;j<=n;j++)
//            cout<<w[i][j]<<" ";
//        cout<<endl;}
///format
    for(int i=1;i<=n;i++)
        cout<<i<<" ";
    cout<<endl;
    for(int i=1;i<=n;i++)
    cout<<"--";
    cout<<endl;
    //

    BackTrack(1);

    cout<<sum;
    return 0;
}

//void backtrack(int k)
//{
//    if(到达边界) 更新或者输出结果
//    else
//    {
//        对于每一种可能进行操作(for循环 i)
//        {
//            if(限定条件合法)
//                backtrack(k+i);
//        }
//    }
//
//}.
//in1
//5
//4
//9
//1 2
//1 3
//1 4
//2 3
//2 4
//2 5
//3 4
//3 5
//4 5
//out1
//1 2 3 4 5
//----------
//1 2 3 4 1
//1 2 4 3 1
//1 3 2 4 1
//1 3 4 2 1
//1 4 2 3 1
//1 4 3 2 1
//2 1 3 4 2
//2 1 4 3 2
//2 3 1 4 2
//2 3 4 1 2
//2 4 1 3 2
//2 4 3 1 2
//3 1 2 4 3
//3 1 4 2 3
//3 2 1 4 3
//3 2 4 1 3
//3 4 1 2 3
//3 4 2 1 3
//4 1 2 3 4
//4 1 3 2 4
//4 2 1 3 4
//4 2 3 1 4
//4 3 1 2 4
//4 3 2 1 4
//24
//in2
//4 3 4
//1 2
//1 4
//2 3
//4 3
//1 2 3 4
//--------
//1 2 1 2
//1 2 1 3
//1 2 3 2
//1 3 1 2
//1 3 1 3
//1 3 2 3
//2 1 2 1
//2 1 2 3
//2 1 3 1
//2 3 1 3
//2 3 2 1
//2 3 2 3
//3 1 2 1
//3 1 3 1
//3 1 3 2
//3 2 1 2
//3 2 3 1
//3 2 3 2
//18

五、实验结果与分析:
通过运行程序验证程序的正确性,给出程序运行结果,分析程序出现的bug的原因,调试程序过程种出现的错误和解决方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值