写在前面
好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。
笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!
目前在看的教程:
北京理工大学ACM冬季培训课程
算法竞赛入门经典/刘汝佳编著.-2版
点击下载
课程刷题点
Virtual Judge
刷题代码都会放在github上,欢迎一起学习进步!
今天看了北理的简单数据结构一讲,实在是太拉胯了,讲的很差劲。无奈之下只能直接做紫书,打算看着抄一遍答案过一遍思路后去做洛谷。紫书是真的难啊…
6 - 2 Rails
铁轨的中转站C就是栈。通过模拟栈的运作方式逐步对比,看是否有不对应的部分。
抓住出栈后无法返回的特性,从左往右对比逐步对比,因为最左边的必须先出栈。具体来说,车厢不断进栈,如果进栈的车厢和给定出栈顺序最左侧的车厢编号一样,则出栈;否则继续进栈。如果模拟过程出现问题,即执行完过程后栈(C)不为空,则失败。
#include <iostream>
#include <sstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <queue>
#include <map>
#include <stack>
#include <unordered_map>
using namespace std;
typedef long long ll;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
#define LOCAL
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
int arr[1005];
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int n;
while (scanf("%d", &n) == 1 && n)
{
while (true)
{
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
if (arr[0] == 0)
goto end;
}
stack<int> s;
int step = 0;
bool fg = false;
for (int i = 1; i <= n; i++)
{
s.push(i);
if (s.top() == arr[step])
{
s.pop();
step++;
fg = true;
}
while (fg)
{
if (!s.empty() && s.top() == arr[step])
{
s.pop();
step++;
}
else
fg = false;
}
}
if (s.empty())
printf("Yes
");
else
printf("No
");
}
end:
cout << endl;
}
return 0;
}
6 - 3 Matrix Chain Multiplication
依旧是栈的运用。实际上问题的核心是分割表达式(括号)。用结构体表示矩阵,每次遇到字母则入栈,遇到)时处理栈,pop出两个矩阵,生成新的矩阵入栈,知道栈中只剩下一个新的矩阵。在这个过程中累加乘法次数。
#include <iostream>
#include <sstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <queue>
#include <map>
#include <stack>
#include <unordered_map>
using namespace std;
typedef long long ll;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
#define LOCAL
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
struct Matrix{
int row;
int col;
char id;
Matrix(){
id = ' ';
row = 0;
col = 0;
}
Matrix(int row, int col){
id = ' ';
this->row = row;
this->col = col;
}
}m[30];
// unordered_map<string, int> id; // 矩阵id向编号的映射
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> m[i].id >> m[i].row >> m[i].col;
string str;
stack<Matrix> s;
end:
while(cin >> str){
int times = 0;
for(int i = 0; i < str.length(); i++){
if(str[i] == '(')
continue;
else if(str[i] == ')')
{
Matrix m2 = s.top();
s.pop();
Matrix m1 = s.top();
s.pop();
if(m1.col != m2.row)
{
printf("error
");
goto end;
}
else
{
times += m1.row * m1.col * m2.col;
s.push(Matrix(m1.row, m2.col));
}
}
else
s.push(m[str[i] - 'A']);
}
printf("%d
", times);
}
return 0;
}
6 - 4 Broken Keyboard
因为问题是不断在数组中插入心的字符,而且插入的位置可以是中间,所以使用链表。设定一个鼠标光标,遇到home时光标回退到0,否则跟随输入的字符前进,设定一个last表示最后一个字符的位置,遇到end光标前进到last处。
紫书的解法有点难理解,用的是类似游标的东西,用next数组表示位置i下一个字符在原先的字符串数组中的位置。说实话我也没看懂代码…
还有直接用list做的,就比较容易理解了,没那么恶心…把其他大佬的list做法copy下来。
#include <iostream>
#include <sstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <queue>
#include <map>
#include <stack>
#include <list>
#include <unordered_map>
using namespace std;
typedef long long ll;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
#define LOCAL
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
int nex[100005];
int last, current;
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
string text;
while (cin >> text)
{
text = " " + text;
int n = text.size() - 1;
last = current = 0;
nex[0] = 0;
for (int i = 1; i <= n; i++)
{
char ch = text[i];
if (ch == '[')
current = 0;
else if (ch == ']')
current = last;
else
{
nex[i] = nex[current];
nex[current] = i;
if (current == last)
last = i;
current = i;
}
// for (int i = 0; i <= n; i++)
// cout << nex[i];
// cout << endl;
}
for (int i = nex[0]; i != 0; i = nex[i])
printf("%c", text[i]);
printf("
");
}
return 0;
}
// string s;
// int main() {
// while (cin >>s) {
// list<char> l;
// auto p = l.begin(); // 初始化
// for (auto ch : s) {
// if (ch == '[') p = l.begin(); // 开头
// else if (ch == ']') p = l.end(); // 结尾
// else {
// p = l.insert(p, ch); // 返回插入数据的迭代器
// p ++; // 后移一位
// }
// }
// for (auto ch : l) cout <<ch; // 输出
// puts("");
// }
// return 0;
// }
6- 5 Boxes in a Line
双向链表。因为需要往左插入、往右插入,所以要双向。这个有点像TSP、VRP了,老熟人了。写VRP的时候反正都是用的ArrayList,就很好写。紫书 中通过一个函数简单实现了一个双向链表,实际上只要两个数组left和right标记编号为i位置左右两个位置的下标就够了,速度应该快很多。
还要注意一点,反转操作不需要实现,只需要标记一下,后续对应的指令取反就好。
insert时如果相邻需要特殊操作。没什么问题。
#include <iostream>
#include <sstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <string>
#include <set>
#include <queue>
#include <map>
#include <stack>
#include <unordered_map>
using namespace std;
typedef long long ll;
#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000
#define LOCAL
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
int rt[100005];
int lt[100005];
void link(int L, int R) // 连接l、r点, 注意左右顺序.注意没有修改原本的连接
{
rt[L] = R;
lt[R] = L;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int n, m, kase = 0;
while (cin >> n >> m)
{
for (int i = 1; i < n; i++)
{
lt[i] = i - 1;
rt[i] = i + 1;
}
lt[0] = n;
rt[0] = 1;
lt[n] = n - 1;
rt[n] = 0;
int op, inv = 0, x, y;
while (m--)
{
cin >> op >> x >> y;
// 相邻时特殊处理
if (op == 3 && rt[y] == x)
swap(x, y);
if (op != 3 && inv)
op = 3 - op;
if (op == 1 && x == lt[y])
continue;
if (op == 2 && x == rt[y])
continue;
int lx = lt[x], rx = rt[x], ly = lt[y], ry = rt[y];
if (op == 1)
{
link(lx, rx);
link(ly, x);
link(x, y);
}
else if (op == 2)
{
link(lx, rx);
link(y, x);
link(x, ry);
}
else if (op == 3)
{
if (rt[x] == y)
{
link(lx, y);
link(y, x);
link(x, ry);
}
else
{
link(lx, y);
link(y, rx);
link(ly, x);
link(x, ry);
}
}
}
int nex = 0;
ll ans = 0;
for (int i = 1; i <= n; i++)
{
nex = rt[nex];
if (i % 2 == 1)
ans += nex;
}
// 偶数个时反转,所求即为偶数下标和
if (inv && n % 2 == 0)
ans = (ll)(n * (n + 1) / 2) - ans;
printf("Case %d: %lld
", ++kase, ans);
}
}