好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。
笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!
目前在看的教程:
北京理工大学ACM冬季培训课程
算法竞赛入门经典/刘汝佳编著.-2版可以在这里下载->github
课程刷题点
Virtual Judge
刷题代码都会放在github上,欢迎一起学习进步!
类似DFS,递归遍历。
tips:解答树的节点大多数源于最后两层,以上部分可以忽略不计。
北理DFS一讲中提到过。按字典序最小开始生成全排列,需要定义<。
昨天也提到过,与或非计算符可以轻松实现集合操作。
特点:
昨天做DFS的素数环,可以生成素数表预处理:
回溯法的题目因为昨天做过几道,今天就不细作了。tips:计算量较大的全排列题目都可以考虑用DFS加上适当的剪枝方法做。剪枝方法有些没那么明显,需要适当思考。
可行解DFS,最短路BFS。
标记一下八数码问题,本来想做一下,但好像不是UVA上的,书上介绍很全,就不具体写了。
mark是因为以前JS做过一个八数码的练习小游戏,那时候还不知道这叫八数码…下次可以考虑把算法加进去。八数码问题JS游戏版
Mark一下解决算法:
最短路+BFS。图的每一个状态表示一个点,如左图为264137058,表示一个点,有两条无向边连接164037157以及164137058两个点。点的个数就有9的全排列个。问题就是求初始点到1234567890的最短路。最短路用BFS比较好。
查重优化:
倒水问题也可以建模为一幅有向图。每一杯杯子的水量为一个向量,构成一个三元组表示一个状态——也就是点。
那么用类似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;
}
直接BFS容易超时,需要优化。一个优化方向是把空格单独提取出来,形成新的图,因为题目告诉我们障碍物很多。
以前没搞过这种方法,就百度查了一下,然后还是写了个半死,主要是写太多bug了…具体看代码吧,有点累了…
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef pair
#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;
}
mark一下A*,要学一下。
本来想好好做一下这题,但做了好一会儿还是做错了,实在不想做了,哎,心情好再回来补吧,今天先到这里…