回溯法与分支限界法
一、稳定婚姻问题(必做)
有n对男女要配成n对夫妇,其中第i位男士对第j位女士的爱恋程度为p[i][j],第i位女士对第j位男士的爱恋程度为q[i][j],显然p[i][j]不一定等于q[j][i]。将他们组成n对夫妇,如果存在这样一个男男士和女士,这两人不是夫妇,但他们互相喜欢的程度胜过对自己的配偶的喜爱,这样的n对夫妇称为不稳定婚姻。如果不存在这样的男士和女士,则称为稳定婚姻。请利用回溯法求解n对男女的所有稳定婚姻配对和最佳稳定婚姻配对(n对夫妇婚姻总体的满意程度:S= Σ p[i][i’] *q[i’][i],其中第i位男士的配偶为第i’位女士)。
注意: 1.假设p[i][j]、q[i][j]的取值为0.3-1.0,值越大代表满意度越高。
2.需描述回溯法算法思想(解空间、状态树、搜索方法、限界函数)
3.严格按照回溯法的算法框架编写
二、0-1背包问题(2选1)
给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,如何选择才能使得物品的总价格最高?请利用优先队列式分支限界法求解该问题。
注意: 1、优先队列结构可以使用库函数
2.需描述回溯法算法思想(解空间、状态树、搜索方法、限界函数)
3.严格按照分支限界法的算法框架编写
三、N皇后问题(2选1)
在N*N的棋盘上放置彼此不受攻击的N个皇后。按照国际象棋的规则,皇后可以攻击与之处于同一行或同一列或同一斜线上的棋子。N皇后的问题等价于在N*N大小的棋盘中放置N个皇后,任何2个皇后都不放在同一行或同一列或同一斜线上。使用队列式分支限界法,求出N个皇后的一种放置方案。
1、队列结构可以使用库函数
2.需描述回溯法算法思想(解空间、状态树、搜索方法、限界函数)
3.严格按照分支限界法的算法框架编写
上机13 回溯法解决婚姻匹配文体
- 回溯法
- 算法思想:
可以使用回溯法解决婚姻匹配文题。具体如下:通过建立解空间树,以数的层次作第几个男士,以第几个枝丫作第几个女士。并且用递归的方式以深搜的方法探索各叶子节点。通过限界函数减去枝叶:
- 先该女士再此之前是否已经配对过,如果已经配对,返回上一层;如果没有配对,进入2。
- 判断已经配对的男女与新配对的男女是否存在稳定性。如果不稳定,放回上一层;如果稳定。把该新配对的男女存起来。
最后根据是否到达叶子节点为终止条件,如果到达叶子节点,返回上一层。利用公式计算:n对夫妇婚姻总体的满意程度:S= Σ p[i][i’] *q[i’][i](其中第i位男士的配偶为第i’位女士)计算出最为满意的一组。
2、实现代码及运行结果
程序源代码如下:
/*
------------------回溯法解决稳定婚姻问题-------------------------
*/
#include <iostream>
#include <cstring>
using namespace std;
#define MAXSIZE 30 //男女最大配对对数
double mlove[MAXSIZE][MAXSIZE]; //男喜欢女的程度数组
double wmlove[MAXSIZE][MAXSIZE]; //女喜欢男的程度数组
int n; //男女配对对数
int mo[MAXSIZE]; //存储配对男的数组
int s = -1;
double sum[MAXSIZE];
int b = -1;
int a = 0;
//记录每组男女配对总和
void love_sum(void)
{
b++;
for (int i = 0; i < n; i++)
{
sum[b] = sum[b] + mlove[i][mo[i]] * wmlove[mo[i]][i];
}
cout << "该组男女配对爱的总和是:" << sum[b] << endl << endl;
}
void cout_best(void)
{
int max = 0;
for (int i = 0; i <= b; i++)
{
if (max < sum[i])
{
max = sum[i];
}
}
cout << "最优男女配对情况是:第" << max+1 << "组数据" << endl;
}
void output(void)
{
a++;
cout <<"第" << a << "组男女配对情况是:" << endl;
for (int i = 0; i < n; i++)
{
cout << "第" << i+1 << "男子配对第" << mo[i]+1 << "个女子" << endl;
}
love_sum();
}
//参数 k:第几个男士 参数 t:第几个女士
int limit(int k,int t)
{
//判断女生是否已经配对
for (int i = 0; i < k; i++)
{
if (t == mo[i])
{
return 0; //不稳定
}
}
//判断男女配对是否稳定
for (int i = 0; i < k; i++)
{
if ((wmlove[t][i] > wmlove[t][k] && mlove[i][t] > mlove[i][mo[i]]) || (wmlove[mo[i]][k] > wmlove[mo[i]][i] && mlove[k][mo[i]] > mlove[k][t]))
{
return 0; //不稳定
}
}
return 1; //稳定
}
void backtrack(int t)
{
if(t == n)
{
output();
return;
}
for(int i = 0; i < n; i++)
{
if(limit(t, i))
{
mo[t] = i;
backtrack(t+1);
}
}
}
int main()
{
//初始化数组
memset(mlove, 0, sizeof(int)*MAXSIZE*MAXSIZE);
memset(wmlove, 0, sizeof(int)*MAXSIZE*MAXSIZE);
memset(mo, 255, sizeof(int)*MAXSIZE);
memset(sum, 0, sizeof(int)*MAXSIZE);
cout << "请输入男女配对对数:________\b\b\b\b\b";
cin >> n;
int t;
cout << "请输入男生对女生的喜爱程度" << endl;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cin >> mlove[i][j];
}
}
cout << "请输入女生对男生的喜爱程度" << endl;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
cin >> wmlove[i][j];
}
}
backtrack(0);
cout_best();
system("pause");
return 0;
}
实现结果:
- 当输入结果男女配对个数为3,
男生对女生的喜爱程度是:
(列表示男生,行表示女生)
0.6 0.7 0.5
0.7 0.8 0.9
0.9 0.8 0.7
女生对男生的喜爱程度是:
(列表示女生,行表示男生)
0.4 0.6 0.5
0.5 0.7 0.8
0.7 0.6 0.5
达到预期结果
- 当输入结果男女配对对数是:2
男生对女生的喜爱程度是:
(列表示男生,行表示女生)
0.1 0.3
0.2 0.4
女生对男生的喜爱程度是:
(列表示女生,行表示男生)
0.2 0.3
0.3 0.5
达到预期结果
3、复杂度分析及算法改进
(1)时间复杂度
算法中,给n对男女稳定婚姻配对,主要耗费时间的是解空间树,解空间是排列树,耗费的时间是o(n!);而遍历每个结点都需要通过限界函数判断,限界函数的耗费的时间是o(n)。
总的时间复杂度是
T(n)= o(n!) * o(n)。
所以T(n)= o(n*n!)
- 空间复杂度
算法中,耗费空间的是存储男女喜欢程度的二维数组,耗费空间是o(n^2)和解空间树耗费的空间o(n^2)
所以s(n)= o(n^2).
(3)算法改进
目前想不到改进方法