算法入门刷题笔记 紫书第七章暴力读书笔记

写在前面

好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。
笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!

目前在看的教程:
北京理工大学ACM冬季培训课程

算法竞赛入门经典/刘汝佳编著.-2版可以在这里下载->github

课程刷题点
Virtual Judge

刷题代码都会放在github上,欢迎一起学习进步!

U6 暴力

例题一览

算法入门刷题笔记 紫书第七章暴力读书笔记_第1张图片

解答树

算法入门刷题笔记 紫书第七章暴力读书笔记_第2张图片

类似DFS,递归遍历。

算法入门刷题笔记 紫书第七章暴力读书笔记_第3张图片

tips:解答树的节点大多数源于最后两层,以上部分可以忽略不计。

next_permutation

算法入门刷题笔记 紫书第七章暴力读书笔记_第4张图片

算法入门刷题笔记 紫书第七章暴力读书笔记_第5张图片

北理DFS一讲中提到过。按字典序最小开始生成全排列,需要定义<。

二进制表示集合

昨天也提到过,与或非计算符可以轻松实现集合操作。

算法入门刷题笔记 紫书第七章暴力读书笔记_第6张图片

算法入门刷题笔记 紫书第七章暴力读书笔记_第7张图片

回溯法

特点:

算法入门刷题笔记 紫书第七章暴力读书笔记_第8张图片

昨天做DFS的素数环,可以生成素数表预处理:

在这里插入图片描述

回溯法的题目因为昨天做过几道,今天就不细作了。tips:计算量较大的全排列题目都可以考虑用DFS加上适当的剪枝方法做。剪枝方法有些没那么明显,需要适当思考。

路径寻找

算法入门刷题笔记 紫书第七章暴力读书笔记_第9张图片

可行解DFS,最短路BFS。

算法入门刷题笔记 紫书第七章暴力读书笔记_第10张图片

八数码问题

标记一下八数码问题,本来想做一下,但好像不是UVA上的,书上介绍很全,就不具体写了。

mark是因为以前JS做过一个八数码的练习小游戏,那时候还不知道这叫八数码…下次可以考虑把算法加进去。八数码问题JS游戏版

Mark一下解决算法:

最短路+BFS。图的每一个状态表示一个点,如左图为264137058,表示一个点,有两条无向边连接164037157以及164137058两个点。点的个数就有9的全排列个。问题就是求初始点到1234567890的最短路。最短路用BFS比较好。

查重优化:

  1. vis数组判断是否访问改为编码解码,化为整数。
  2. 哈希技术。设计哈希函数,将节点映射到整数。哈希函数可能导致两点哈希值相同,所以需要设计链表的结构,对相同哈希值的节点进行插入。合理的哈希函数可以避免大量哈希值相同的情况,从而提高效率。如果哈希值相同情况过多,哈希表会退化为链表查找速度就很慢。其实第一种方法相当于设计了无重复的哈希函数。
  3. 使用STL的set。(STL, yyds!)(三个版本中set速度最慢)

7-8 Fill

算法入门刷题笔记 紫书第七章暴力读书笔记_第11张图片

倒水问题也可以建模为一幅有向图。每一杯杯子的水量为一个向量,构成一个三元组表示一个状态——也就是点。

算法入门刷题笔记 紫书第七章暴力读书笔记_第12张图片

那么用类似BFS的方法遍历整个图,把每个点对应的最短路都保存下来,相当于单源最短路,就能得到最后的答案。实际上算法更像Dijstra,Dijstra本身就很像BFS,求的也是单源最短路。题目要求的最短是水量最少,所以不是每次取出步数最少的结点进行扩展,只需要把队列queue换成优先队列priority_queue,设置<为水量即可,这个方法在昨天B题也用到过。

书中代码中间的一些细节也做的很好,值得仔细看看。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
#define LOCAL

struct Node
{
	int v[3]; // 三个杯子的水量
	int dist; // 倒水量
	bool operator<(const Node &rhs) const 
	{ 
		return dist > rhs.dist; 
	}
};

const int maxn = 200 + 5;
int vis[maxn][maxn], cap[3], ans[maxn];

void update_ans(const Node &u)
{
	for (int i = 0; i < 3; i++)
	{
		int d = u.v[i];
		if (ans[d] < 0 || u.dist < ans[d])
			ans[d] = u.dist;
	}
}

void solve(int a, int b, int c, int d)
{
	cap[0] = a;
	cap[1] = b;
	cap[2] = c;
	memset(vis, 0, sizeof(vis));
	memset(ans, -1, sizeof(ans));
	priority_queue<Node> q;
	Node start;
	start.dist = 0;
	start.v[0] = 0;
	start.v[1] = 0;
	start.v[2] = c;
	q.push(start);
	vis[0][0] = 1;
	while (!q.empty())
	{
		Node u = q.top();
		q.pop();
		update_ans(u);
		if (ans[d] >= 0)
			break;
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				if (i != j)
				{
					if (u.v[i] == 0 || u.v[j] == cap[j])
						continue;
					int amount = min(cap[j], u.v[i] + u.v[j]) - u.v[j];
					Node u2;
					memcpy(&u2, &u, sizeof(u));
					u2.dist = u.dist + amount;
					u2.v[i] -= amount;
					u2.v[j] += amount;
					if (!vis[u2.v[0]][u2.v[1]])
					{
						vis[u2.v[0]][u2.v[1]] = 1;
						q.push(u2);
					}
				}
	}
	while (d >= 0)
	{
		if (ans[d] >= 0)
		{
			printf("%d %d\n", ans[d], d);
			return;
		}
		d--;
	}
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
	// freopen("data.out", "w", stdout);
#endif
	int kase, a, b, c, d;
	scanf("%d", &kase);
	while (kase--)
	{
		scanf("%d%d%d%d", &a, &b, &c, &d);
		solve(a, b, c, d);
	}
	return 0;
}

7-10 The Morning after Halloween

算法入门刷题笔记 紫书第七章暴力读书笔记_第13张图片

算法入门刷题笔记 紫书第七章暴力读书笔记_第14张图片
在这里插入图片描述

直接BFS容易超时,需要优化。一个优化方向是把空格单独提取出来,形成新的图,因为题目告诉我们障碍物很多。

以前没搞过这种方法,就百度查了一下,然后还是写了个半死,主要是写太多bug了…具体看代码吧,有点累了…

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long ll;
typedef pair pii;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
// #define LOCAL

struct State
{
int posId[3]; // 小鬼的位置
int dis; // 步数
State()
{
posId[0] = 0;
posId[1] = 0;
posId[2] = 0;
}
};

struct Node
{
int x, y;
int dir[5];
int d;
} G[500]; // 空格节点

int dx[] = {0, 0, 0, -1, 1};
int dy[] = {0, 1, -1, 0, 0};
const int maxn = 500;
int n, m, g, cnt;
int vis[maxn][maxn][maxn]; //是否经过
char graph[maxn][maxn]; //原地图
int dest[3], start[3];
int id[maxn][maxn]; //坐标到节点编号

bool conflict(const int a, const int b, const int na, const int nb)
{
return na == nb || (na == b && nb == a);
}

void solve()
{
memset(vis, 0, sizeof(vis));
queue q;
State s;
for (int i = 0; i < 3; i++)
s.posId[i] = start[i];
q.push(s);
vis[s.posId[0]][s.posId[1]][s.posId[2]] = 1;
while (!q.empty())
{
State u = q.front();
q.pop();

	// cout << u.posId[0] <<" "<< u.posId[1] << " " << u.posId[2] << endl;

	if (u.posId[0] == dest[0] && u.posId[1] == dest[1] && u.posId[2] == dest[2])
	{
		cout << u.dis << endl;
		break;
	}
	for (int i = 0; i < G[u.posId[0]].d; i++)
	{
		int na = G[u.posId[0]].dir[i];
		if (g == 1)
		{
			if (vis[na][0][0])
				continue;
			State nu;
			nu.dis = u.dis + 1;
			nu.posId[0] = na;
			q.push(nu);
			vis[na][0][0] = 1;
		}
		else
			for (int j = 0; j < G[u.posId[1]].d; j++)
			{
				int nb = G[u.posId[1]].dir[j];
				if (conflict(u.posId[0], u.posId[1], na, nb))
					continue;
				if (g == 2)
				{
					if (vis[na][nb][0])
						continue;
					State nu;
					nu.dis = u.dis + 1;
					nu.posId[0] = na;
					nu.posId[1] = nb;
					q.push(nu);
					vis[na][nb][0] = 1;
				}
				else
					for (int k = 0; k < G[u.posId[2]].d; k++)
					{
						int nc = G[u.posId[2]].dir[k];
						if (conflict(u.posId[0], u.posId[2], na, nc))
							continue;
						if (conflict(u.posId[2], u.posId[1], nc, nb))
							continue;
						if (vis[na][nb][nc])
							continue;
						State nu;
						nu.dis = u.dis + 1;
						nu.posId[0] = na;
						nu.posId[1] = nb;
						nu.posId[2] = nc;
						q.push(nu);
						vis[na][nb][nc] = 1;
					}
			}
	}
}

}

int main()
{
#ifdef LOCAL
freopen(“data.in”, “r”, stdin);
// freopen(“data.out”, “w”, stdout);
#endif
while (scanf("%d%d%d", &n, &m, &g) == 3 && n)
{
int temp = n;
n = m;
m = temp;
getchar();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
graph[i][j] = getchar();
getchar();
}

	memset(dest, 0, sizeof(dest));
	cnt = 0;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			if (graph[i][j] != '#')
			{
				G[cnt].x = i;
				G[cnt].y = j;
				id[i][j] = cnt;
				if (islower(graph[i][j]))
					start[graph[i][j] - 'a'] = cnt;
				if (isupper(graph[i][j]))
					dest[graph[i][j] - 'A'] = cnt;
				cnt++;
			}

	for (int i = 0; i < cnt; i++)
	{
		G[i].d = 0;
		for (int j = 0; j < 5; j++)
		{
			int nx = G[i].x + dx[j], ny = G[i].y + dy[j];
			if (graph[nx][ny] != '#')
				G[i].dir[G[i].d++] = id[nx][ny];
		}
	}

	solve();
}
return 0;

}

迭代加深搜索

算法入门刷题笔记 紫书第七章暴力读书笔记_第15张图片

算法入门刷题笔记 紫书第七章暴力读书笔记_第16张图片

mark一下A*,要学一下。

算法入门刷题笔记 紫书第七章暴力读书笔记_第17张图片

本来想好好做一下这题,但做了好一会儿还是做错了,实在不想做了,哎,心情好再回来补吧,今天先到这里…

你可能感兴趣的:(算法入门刷题笔记 紫书第七章暴力读书笔记)