蛮力法
蛮力法是检查探索空间中的每一个解直到找到问题的解。
蛮力法所依赖的基本技术:扫描技术。
关键:依次处理所有元素。
基本的扫描技术:遍历。
对于一个具有n个元素的集合,其子集数量是2^n,所以,不论生成子集的算法效率有多高,蛮力法都会导致一个Ω(2 ^n)的算法。
蛮力法的应用
解决01背包问题
#include <iostream>
#define MAX 100
using namespace std;
int n, C;//物品个数 背包容量
int w[MAX], v[MAX], flag[MAX],r[MAX];//重量 价值 标记 结果
int sum_w, sum_v;//背包重量 价值
int cnt;//背包中物品个数
//int c = 1;//用来核验考虑的次数是否为16
void record(int sum_v) {
cnt = 0;
r[0] = sum_v;//r[0]存放目前找到的最大价值
for (int i = 1; i <= n; i++) {
if (flag[i] == 1)
r[++cnt] = i;
}
}
void findRet(int x) {
if( (x == n + 1) ) {//flag[n]=1的情况,考虑是否需要record
//cout << c++<<endl;
if((sum_w <= C) && (sum_v > r[0]))record(sum_v);//记录最大价值的情况
return;
}
for (int i = x; i <= n; i++) {
//循环把物品x到n依次考虑一遍
//下面步骤把第x个放进去,再考虑第x+1个要不要放进去
sum_w += w[i];
sum_v += v[i];
flag[i] = 1;
findRet(i + 1);
//如果第x放进去已经超重了,那么在传入参数x+1时,就直接返回,下面步骤会将第x个物品取出
//如果第x个放进去没有超重,那么在传入参数x+1时,会重复同样的情况,即放入x+1.考虑x+2
sum_w -= w[i];
sum_v -= v[i];
flag[i] = 0;
if (i == n) {//当flag[n]=1的情况考虑之后,将flag[n]置为0时,需要考虑是否record
//cout << c++<<endl;
if ((sum_w <= C) && (sum_v > r[0]))record(sum_v);
}
}
}
int main()
{
cout << "请输入物品的数量:" << endl;
cin >> n;
cout << "请输入背包最大容量:" << endl;
cin >> C;
cout << "请分别输入每个物品的重量和价值:" << endl;
for (int i = 1; i <= n; i++)
{
flag[i] = 0;
cin >> w[i] >> v[i];
}
findRet(1);//传参是1,从i=1开始考虑
cout << "选择的物品为" << endl;
for (int i = 1; i <= cnt; i++)
{
cout << r[i] << "号物品,重量为" << w[r[i]] << "价值为" << v[r[i]] << endl;
}
cout << "最大总价值为" << r[0] << endl;
return 0;
}
测试数据:
输入:
4
10
7 42
3 12
4 40
5 25
输出:
采用蛮力法求解0/1背包问题的时间复杂度为:T(n)=O(2^n)
任务分配问题
#include <iostream>
#include <vector>
#define MAX 10
using namespace std;
int n;
int cost[MAX][MAX];//成本矩阵
bool used[MAX];//1表示员工已使用 0表示未使用
vector<int> best_assignment;
vector<int> cur_assignment;
int min_cost = INT_MAX;
int cur_cost = 0;
void dfs(int task) {
if (task > n) {
if (min_cost > cur_cost) {//当循环到task=n时,继续传入参数n+1,此时进行比较,然后返回
min_cost = cur_cost;
best_assignment = cur_assignment;
}
return;
}
if (task == 1) {
cur_assignment.push_back(0);
}
for (int worker = 1; worker <= n; worker++) {
if (!used[worker]) {//直到为当前的task找到一个worker才能dfs(task+1)
used[worker] = 1;
cur_assignment.push_back(worker);
cur_cost += cost[task][worker];
dfs(task + 1);
cur_assignment.pop_back();
cur_cost -= cost[task][worker];
used[worker] = 0;
//将当前worker置为未使用,然后worker++,即为当前task找到下一个可用的worker
}
}
}
int main() {
cin >> n;//n个人分配n个工作
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> cost[i][j];//表示第i份工作交给第j个人需要的成本
}
}
vector<int> cur_assignment;
dfs(1);//从第一份工作开始分配,当前成本为0
cout << min_cost << endl;
for (int i = 1; i <= n; i++) {
cout << "将第" << i << "份工作分配给第" << best_assignment[i] << "个人" << endl;//best_assignment[i]表示第i个工作分配给哪个人
}
return 0;
}
测试数据:
输入:
3
9 2 7
6 4 3
5 8 1
输出:
采用蛮力法求解任务分配问题的时间复杂度为:T(n)=O(n!)
解决哈密顿回路问题
#include<iostream>
#include<vector>
#define MAX 10
using namespace std;//注意在头文件之后加上namespace,否则定义vector的时候报错
int n, cur, cnt = 1;
bool graph[MAX][MAX];
vector <int> path;
bool flag[MAX];
void dfs(int cur) {
if (cnt == n && graph[cur][1]) {//if (cnt == n && graph[cur][2])
for (int i = 1; i <= n; i++) {
cout << path[i] << " ";//打印可行的路径
}
cout << 1 << endl;
//cout << 2<< endl;
return;
}
for (int i = 1; i <= n; i++) {
if (!flag[i] && graph[cur][i] != 0) {
flag[i] = 1;
path.push_back(i);
cnt++;
dfs(i);//当前节点变成了第i个节点
flag[i] = 0;
path.pop_back();
cnt--;
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> graph[i][j];
path.push_back(0);
path.push_back(1);//需要注意选中的节点1放入path中
//path.push_back(2);
flag[1] = 1;//将节点1的flag值设置为1,表示已选择
//flag[2] = 1;
dfs(1);
//dfs(2);
return 0;
}
测试数据:
输入:
5
0 1 1 1 0
1 0 1 1 1
1 1 0 1 0
1 0 1 0 1
0 1 0 1 0
输出:
修改参数可以得到从2出发的可行路径:
蛮力法求解哈密顿回路在最坏情况下需要考察所有顶点的排列对象,其时间复杂性为O(n!)
解决TSP问题
#include<iostream>
#include<vector>
#define MAX 10
using namespace std;//注意在头文件之后加上namespace,否则定义vector的时候报错
int n, cur, cnt = 1;
int graph[MAX][MAX];//在哈密顿问题时设置的是bool类型,此时要改成int类型存放代价
vector <int> path, best_path;
bool flag[MAX];
int cur_cost, min_cost = 99999;
void dfs(int cur) {
if (cnt == n && graph[cur][1]) {
cur_cost += graph[cur][1];
for (int i = 1; i <= n; i++) {
cout << path[i] << " ";//打印可行的路径
}
cout << 1 << endl;
cout << "cost: " << cur_cost << endl;
if (min_cost > cur_cost) {
min_cost = cur_cost;
}
cur_cost -= graph[cur][1];
return;
}
for (int i = 1; i <= n; i++) {
if (!flag[i] && graph[cur][i] != 0) {
flag[i] = 1;
path.push_back(i);
cnt++;
cur_cost += graph[cur][i];
dfs(i);//当前节点变成了第i个节点
flag[i] = 0;
path.pop_back();
cur_cost -= graph[cur][i];
cnt--;
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
{
cin >> graph[i][j];
}
}
path.push_back(0);
path.push_back(1);//需要注意选中的节点1放入path中
flag[1] = 1;//将节点1的flag值设置为1,表示已选择
dfs(1);
cout << "min_cost: " << min_cost << endl;
return 0;
}
测试数据:
输入:
4
0 3 6 7
12 0 2 8
8 6 0 2
3 7 6 0
输出: