写在前面
好久没更新公众号和博客了,因为最近在研究新的方向,所以很少发文。
笔者接触编程只有一年,这一年间主要研究启发式算法在运筹学中的应用。但是由于编程基础薄弱,在进一步研究复杂运筹学问题时发现基础算法不过关导致写出的代码运行速度很慢,因此很苦恼。所以决定这个暑假补习一下基础算法,主要是刷一些简单的ACM入门题。偶尔会发一些刷题笔记(偶尔!)。和作者有类似目标的同学可以一起交流共勉!
目前在看的教程:
北京理工大学ACM冬季培训课程
算法竞赛入门经典/刘汝佳编著.-2版可以在这里下载->github
课程刷题点
Virtual Judge
刷题代码都会放在github上,欢迎一起学习进步!
今天继续做紫书。
5 - 6 Team Queue
肯定会想到用队列,如果用一条队列,队列里放置队员的ID,那样不好处理。既然队员都是和同队队友排在一起的,直接用队列表示一个队伍,队列嵌套队列的形式,再用map存储一下队员ID对应的队伍id,就很方便处理了。
unordered_map之前提到过,和map用法差不多,查找比较快。
#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;
}
queue<int> q;
queue<string> ques[1005];
unordered_map<string, int> team;
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int n, kase = 1;
while (cin >> n && n){
printf("Scenario #%d", kase++);
for(int i = 0; i < n; i++){
int m;
cin >> m;
for(int j = 0; j < m; j++){
string id;
cin >> id;
team[id] = i;
}
}
while(true){
string order;
cin >> order;
if(order[0] == 'S')
break;
else if(order[0] == 'E')
{
string id;
cin >> id;
int t = team[id];
if(ques[t].empty())
q.push(t);
ques[t].push(id);
}
else if(order[0] == 'D')
{
int t = q.front();
cout << ques[t].front() << endl;
ques[t].pop();
if(ques[t].empty())
q.pop();
}
}
cout << endl;
}
return 0;
}
5 - 7 Ugly Numbers
注意除了2,3,5之外的素数不算丑数,1算丑数。
按照定义,任何丑数质因数分解后只有2,3,5上的幂非零,所以任何丑数都可以通过丑数乘以2,3,5得到。用优先队列,每次取出未经处理的最小丑数生成新的丑数,再标记已处理,得到的顺序就是从小到大排列的丑数的顺序。
由于可能重复生成,用set记录以存在的丑数。
这里提一下优先队列的排序方法:
//初始化的三个参数分别为:
//存储的数据类型、容器类型(填vector即可)、排序方式。
//升序队列:小顶堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列:大顶堆
priority_queue <int,vector<int>,less<int> >q;
#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;
}
priority_queue<ll, vector<ll>, greater<ll>> pq;
set<ll> s;
void is_new_ugly(ll y)
{
if (!s.count(y))
{
pq.push(y);
s.insert(y);
}
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
pq.push(1);
s.insert(1);
for(int i = 1; i < 1500; i++)
{
ll x = pq.top();
pq.pop();
ll y1 = x * 2;
ll y2 = x * 3;
ll y3 = x * 5;
is_new_ugly(y1);
is_new_ugly(y2);
is_new_ugly(y3);
}
cout << "The 1500'th ugly number is " << pq.top() << "."<< endl;
return 0;
}
5 - 8 Unix ls
先算出分几列,再算分几行。没啥好说的,但是具体处理输出的时候还是蛮麻烦的。
#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 main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int N;
while (cin >> N)
{
string input;
vector<string> files; //存储文件名
int M = 0; //存储最长文件名的字符数
while (N--)
{
cin >> input;
M = max(M, (int)(input.size()));
files.push_back(input);
}
sort(files.begin(), files.end()); //排序
int C = 1 + (60 - M) / (M + 2), R = (files.size() + C - 1) / C; //求列数C和行数R
for (int i = 0; i < 60; ++i) //输出60个'-'字符
printf("-");
puts(""); //换行
for (int i = 0; i < R; ++i)
{ //输出R行
for (int j = 0; j < C; ++j)
{ //每行输出C列
if (i + j * R >= files.size())
continue;
printf("%s", files[i + j * R].c_str());
int len = (j == C - 1) ? M : M + 2;
for (int k = 0; k < len - files[i + j * R].size(); ++k)
printf(" ");
}
puts(""); //换行
}
}
return 0;
}
5 - 9 Database
简单的说找一个矩形,四个角内容分别相同。枚举四个位置肯定太久了,可以是按列枚举,选择两列,在map中存储,键为两列中的内容,值为行数。如果重复则输出。
书中提到map判断字符串比较慢,可以对表格中的字符串先进行预处理,存储为数字。用set存储,lenth为新的id。这个方法之前用到过。
#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;
}
vector<int> table[10005];
map<string, int> id_map; // 字符串->id
int getId(string s) // 获取字符串id,若已存在,直接返回,否则分配id
{
if (!id_map.count(s))
id_map[s] = id_map.size(); // 不存在
return id_map[s];
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int n, m;
while (cin >> n >> m)
{
cin.get();
id_map.clear();
for (int i = 0; i < n; i++)
{
string s, st;
getline(cin, s);
stringstream input(s);
while (getline(input, st, ','))
table[i].push_back(getId(st));
}
bool isPNF = true;
for (int i = 0; i < m - 1; i++)
{
for (int j = i + 1; j < m; j++)
{
map<pair<int, int>, int> pos; // 两列对应字符串标号->行
for (int k = 0; k < n; k++)
{
if (!pos.count({table[k][i], table[k][j]}))
pos[{table[k][i], table[k][j]}] = k;
else
{
printf("NO
%d %d
%d %d
", pos[{table[k][i], table[k][j]}] + 1, k + 1, i + 1, j + 1);
isPNF = false;
goto end;
}
}
}
}
end:
if (isPNF) printf("YES
");
}
return 0;
}
5 - 12 Urban Elevations
后三题题面都比较复杂,就挑了一题题面字比较少的做了下。
这里注意坐标是double类型。因此判断是否被遮挡时无法枚举x值。可以采用“离散化”的方法,即将无限的x坐标分解为有限个x坐标构成的区间。划分区间的方法可以用和为分界点划分。
用到了unique函数,简单记一下用法:
#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 Building
{
int id;
double x, y, w, d, h;
bool operator < (const Building& other) const
{
if (x != other.x)
return x < other.x;
else
return y < other.y;
}
} b[105];
int n;
double x[300];
// 判断建筑id在mid_x所在区间内
bool is_in_section(int id, int mid_x)
{
return (b[id].x < mid_x && (b[id].x + b[id].w) > mid_x);
}
// 判断建筑id在mid_x处是否能被看见
bool is_visible(int id, double mid_x)
{
if (!is_in_section(id, mid_x))
return false;
for (int i = 0; i < n; i++)
{
if (is_in_section(i, mid_x) &&
(b[i].y < b[id].y && b[i].h >= b[id].h))
return false;
}
return true;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
#endif
int kase = 1;
while (cin >> n && n)
{
for (int i = 0; i < n; i++)
{
cin >> b[i].x >> b[i].y >> b[i].w >> b[i].d >> b[i].h;
b[i].id = i + 1;
x[2 * i] = b[i].x;
x[2 * i + 1] = b[i].x + b[i].w;
}
sort(x, x + 2 * n);
sort(b, b + n);
int m = unique(x, x + n * 2) - x; //x坐标排序后去重,得到m个坐标
printf("For map #%d, the visible buildings are numbered as follows:
", kase++);
cout << b[0].id;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < m - 1; j++)
{
if (is_visible(i, (x[j] + x[j + 1]) / 2))
{
printf(" %d", b[i].id);
break;
}
}
}
printf("
");
}
return 0;
}
结语
那么紫书第五章就这样草草的过去了…
看了一下北理,共有12章,要加快速度了。紫书明显比北理难,要考虑先全速刷完北理,打打基础再来挑战紫书…