算法分析与设计知识点复习之蛮力法

蛮力法

蛮力法是检查探索空间中的每一个解直到找到问题的解。

蛮力法所依赖的基本技术:扫描技术。
关键:依次处理所有元素。
基本的扫描技术:遍历。

对于一个具有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
输出:
在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值