Acm入门3:数据结构(第四篇博客)

一:队列,栈,优先队列,并查集等基础数据结构。

二:一些算法竞赛常用的STL

三:一些进阶用法

一 Queue:先进先出

队列常见写法:

#include <bits/stdc++.h>

using namespace std;

 

const int N=100010;

 

int q[N];

int head=1,tail=0;

void push(int x){
q[++tail]=x;

}

int front(){
return q[head];

}

void pop(){
head++;

}

int size()

{
return tail-head+1;

}

bool empty(){
return(tail-head+1)==0;

}

int main(){
 

}

例题:

你吓到我的马了

给定一个 n × m n\times mn×m 的棋盘,棋盘上有很多障碍和一个中国象棋中的马(本题中马的移动方式跟中国象棋中的马完全一致)。

马每次移动首先会选择是上下左右中的某个不被卡马脚(卡马脚的定义在下面)的方向并面朝这个方向,然后再选择跳到左前方或者右前方,其中左前方是前面两格,左边一格的地方,右前方是前面两格,右边一格的地方。

然后马的移动有以下这么几个条件限制:

马不能跳到障碍上或者跳出棋盘

当马在某个方向上的前面一个格子有障碍的话,那么我们称马在这个方向上被卡了马脚

现在对于棋盘上的每个点,你需要输出马最少跳几次能跳到这个地方,如果不可能跳到这个地方则输出

− 1 -1−1.

输入描述:

第一行两个正整数 n , m {n,m}n,m 表示棋盘大小

接下来 n {n}n 行,每行一个长度为 {m}m 的字符串,表示地图

第 i {i}i 行第 j {j}j 列的格子由第 i {i}i 行的字符串的第 j {j}j 个(从 1 {1}1 开始计算)字符决定,若是 . {.}. 则表示是空格子, X {X}X 表示障碍,M {M}M表示马,数据保证马有且只有一个。2 ≤ n , m ≤ 100 2\leq n,m\leq 1002≤n,m≤100

输出描述:

输出 n {n}n 行,每行 m {m}m 个数,第 i {i}i 行第 j {j}j 个数表示跳到第 i {i}i 行第 j {j}j 列至少需要几步,如果不可能跳到则输出 − 1 {-1}−1。

输入

3 3

.M.

.X.

输出

-1 0 -1

-1 -1 -1

1 -1 1

题解:枚举 向量数组

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e2 + 5;

int dx[] = { -1,-2,-2,-1,1,2,2,1 };
int dy[] = { 2,1,-1,-2,-2,-1,1,2 };

struct Node {
	int x, y;
	Node(int x, int y) :x(x), y(y) {}
};

int n, m;
char mp[maxn][maxn];
int vis[maxn][maxn];

bool check(int x, int y) {
	if (x < 1 || x > n || y < 1 || y > m)	return false;
	if (mp[x][y] == 'X' || vis[x][y] > 0)	return false;
	return true;
}

void bfs(int now_x, int now_y) {
	queue<Node>q;
	q.push(Node{ now_x,now_y });
	vis[now_x][now_y] = 1;
	while (!q.empty()) {
		int x = q.front().x;
		int y = q.front().y;
		q.pop();
		for (int i = 0; i < 8; ++i) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			switch (i)
			{
			case 7:case 0:
				if (mp[x][y + 1] == 'X')	continue;break;
			case 1:case 2:
				if (mp[x - 1][y] == 'X')	continue;break;
			case 3:case 4:
				if (mp[x][y - 1] == 'X')	continue;break;
			case 5:case 6:
				if (mp[x + 1][y] == 'X')    continue;break;
			}
			if (check(nx, ny)) {
				q.push(Node{ nx,ny });
				vis[nx][ny] = vis[x][y] + 1;
			}
		}
	}
}

int main() {
	while (cin >> n >> m) {
		memset(vis, 0, sizeof vis);
		int x, y;
		for (int i = 1; i <= n; ++i) {
            string s;	cin >> s;
			for (int j = 1; j <= m; ++j) {
                mp[i][j] = s[j-1];
                if(mp[i][j] == 'M')
                    x=i,y=j;
            }
		}
		bfs(x, y);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= m; ++j) 
				cout << vis[i][j] - 1 << " ";
			puts("");
		}
	}
}

二Stack:后进先出

#imclude <bits/stdc++.h>
using namespace std;
const int N=100010;
int st[N],top;
void push(int x){
st[++top]=x;
}
bool empty()
{
return !top;
}
int size(){
return top;
}
void pop(){
top--;
}

例题1:

Rails

Time Limit: 1000MS Memory Limit: 10000K

Total Submissions: 46161 Accepted: 17522

Description

There is a famous railway station in PopPush City. Country there is incredibly hilly. The station was built in last century. Unfortunately, funds were extremely limited that time. It was possible to establish only a surface track. Moreover, it turned out that the station could be only a dead-end one (see picture) and due to lack of available space it could have only one track.

The local tradition is that every train arriving from the direction A continues in the direction B with coaches reorganized in some way. Assume that the train arriving from the direction A has N <= 1000 coaches numbered in increasing order 1, 2, ..., N. The chief for train reorganizations must know whether it is possible to marshal coaches continuing in the direction B so that their order will be a1, a2, ..., aN. Help him and write a program that decides whether it is possible to get the required order of coaches. You can assume that single coaches can be disconnected from the train before they enter the station and that they can move themselves until they are on the track in the direction B. You can also suppose that at any time there can be located as many coaches as necessary in the station. But once a coach has entered the station it cannot return to the track in the direction A and also once it has left the station in the direction B it cannot return back to the station.

Input

The input consists of blocks of lines. Each block except the last describes one train and possibly more requirements for its reorganization. In the first line of the block there is the integer N described above. In each of the next lines of the block there is a permutation of 1, 2, ..., N. The last line of the block contains just 0.

The last block consists of just one line containing 0.

Output

The output contains the lines corresponding to the lines with permutations in the input. A line of the output contains Yes if it is possible to marshal the coaches in the order required on the corresponding line of the input. Otherwise it contains No. In addition, there is one empty line after the lines corresponding to one block of the input. There is no line in the output corresponding to the last ``null'' block of the input.

Sample Input

5

1 2 3 4 5

5 4 1 2 3

0

6

6 5 4 3 2 1

0

0

Sample Output

Yes

No

Yes

思路:按照给出的出栈顺序,一个一个和和当前栈顶的元素比较,不相等就向当前栈压入一个元素,因为是按照顺序压栈的,

所以总会找到一个当前给出的顺序所在位置值一样的栈顶元素,然后出栈。同时 给出的元素顺序也后移一位 直到给出的顺序遍历完 或者出错。

本题主要是入栈和出栈的过程

三 Priority Queue 优先队列

出队列顺序不一定是入队列的顺序,而是按规定的优先级出队列

实现:堆 相比前面两个复杂,一般不手写,而是使用stl

基于堆实现,堆依靠一个二叉树,这个二叉树满足堆根元素比左右元素都要大或者小。

插入:先插入到二叉树最下部。然后比较它和它父亲的关系,交换父子关系!

弹出:去除根节点后,依次比较两个叶节点的大小。

例题:接水问题洛谷P1190

题目描述

学校里有一个水房,水房里一共装有m个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为 1。

现在有 n名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 1到 n编号,i号同学的接水量为 w_i 。接水开始时,1到 m 号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学 j完成其接水量要求 w_j 后,下一名排队等候接水的同学 k马上接替 j 同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即jj 同学第 x 秒结束时完成接水,则 k 同学第 x+1 秒立刻开始接水。若当前接水人数 n’不足 m,则只有 n’个龙头供水,其它 m−n’个龙头关闭。

现在给出 n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。

输入输出格式

输入格式:

第 1 行 2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。

第 22 行 nn 个整数 w_1,w_2,…,w_n

​ ,每两个整数之间用一个空格隔开,w_i 表示 i号同学的接水量。

输出格式:

1 个整数,表示接水所需的总时间。

输入输出样例

输入样例#1:

5 3

4 4 1 2 1

输出样例#1:

4

输入样例#2:

8 4

23 71 87 32 70 93 80 76

输出样例#2:

163

说明

【输入输出样例 1 说明】

第 1 秒,3人接水。第 1秒结束时,1,2,3 号同学每人的已接水量为 1,3 号同学接完水,4号同学接替 3 号同学开始接水。

第 2 秒,3人接水。第 2 秒结束时,1,2 号同学每人的已接水量为 2,4号同学的已接水量为 1。

第 3 秒,3人接水。第 3 秒结束时,1,2 号同学每人的已接水量为 3,4 号同学的已接水量为2。4 号同学接完水,5 号同学接替 4号同学开始接水。

第 4 秒,3人接水。第 4秒结束时,1,2号同学每人的已接水量为 4,5号同学的已接水量为 11。1,2,5 号同学接完水,即所有人完成接水的总接水时间为 4秒。

【数据范围】

1≤n≤10000,1≤m≤100 且 m≤nm≤n;

1≤w i ≤100。

思路:本质是模拟贪心问题,模拟题目描述的过程

维护每个水龙头最早在什么时刻没有人接水。

#include <bits/stdc++.h>
using namespace std;

const int N=100010;

priority_queue<int>t;
//x→t.top() t.top→t.top+w[x]
//x→min{t[i} t[i]+w[x]

int main(){

}

四 STL

STL是C++中封装好的一套数据结构,可以直接取用。

未完待续

五 Two Pointers

双指针算法和队列:行为没有区别,求区间问题,枚举左端点,不停向右移动。

例题:

高精运算: typedef struct //为方便处理,用结构体 { int len ; long num [1024] ; } HNum ; //万进制高精加法, 注意输出高位补0, printf ("%04d" …) ; void HPlus (HNum &a, HNum &b, HNum &c) { int i, len = a.len > b.len ? a.len : b.len ; memset (&c, 0, sizeof (HNum)) ; for (i = 1 ; i <= len ; i ++) { c.num [i] += a.num [i] + b.num [i] ; if (c.num [i] >= BASE) { c.num [i+1] += c.num [i] / BASE ; c.num [i] %= BASE ; } } c.len = len ; while (c.num [c.len+1] > 0) c.len ++ ; } //万进制高精乘法 void HMul (HNum &a, HNum &b, HNum &c) { int i, j ; memset (&c, 0, sizeof (HNum)) ; for (i = 1 ; i <= a.len ; i ++) for (j = 1 ; j <= b.len ; j ++) { c.num [i+j-1] += a.num [i] * b.num [j] ; //注意+号 if (c.num [i+j-1] >= BASE) { c.num [i+j] += c.num [i+j-1] / BASE ; //注意+号 c.num [i+j-1] %= BASE ; } } c.len = a.len + b.len - 1 ; while (c.num [c.len+1] > 0) // c.len ++ ; } //万进制高精减法 void HSub (HNum &a, HNum &b, HNum &c) { int i, len = a.len ; //保证a >= b memset (&c, 0, sizeof (HNum)) ; for (i = 1 ; i <= len ; i ++) { c.num [i] += a.num [i] - b.num [i] ; //注意+号 if (c.num [i] < 0) { c.num [i+1] -= 1 ; //注意-号 c.num [i] += BASE ; } } c.len = len ; while (c.len > 0 && c.num [c.len] == 0) c.len -- ; } //万进制高精减法, 直接就 long long…. -------------------------------------------------------------------------------- //Fibonacci, Fibo [i] = Fibo [i-1] + Fibo [i-2], Fibo [3] = 3 ; // Catalan数列S[n] = C(2n,n)/(n+1) long Catalan (long n) { long i, x, y ; x = y = 1 ; for (i = 2 ; i <= n ; i ++) x *= i ; for (i = n ; i <= 2*n ; i ++) y *= i ; return y/x/(n + 1) ; } //最小公倍数 long lcm (long a, long b) { return a*b/gdc (a, b) ; } //最大公约数, 辗转相除法 long gdc (long a, long b) { return (a%b == 0)? b : gdc (b, a%b) ; } ------------------------------------------------------------------------------------------------------------ //堆操作 void In (HeapDT dt) //进堆 { int i ; list [++ len] = dt ; i = len ; while (i > 1) //向上调整 { if (list [i].w < list [i/2].w) Swap (i, i/2) ; else break ; i /= 2 ; } } HeapDT Out () //出堆 { HeapDT ret = list [1] ; Swap (1, len) ; //NOTE: 最重要的一步, 最后(最大)一个元素与第一个元素交换 len -- ; //堆长度减1 int i, pa = 1 ; for (i = pa * 2 ; i <= len ; i *= 2) //向下调整 { if (i < len && list [i+1].w < list [i].w) i ++ ; if (list [i].w < list [pa].w) Swap (pa, i) ; else break ; pa = i ; } return ret ; } ------------------------------------------------------------------------------------------------------------ //二分查找, 注意等号 while (low < high) { mid = (low + high) >> 1 ; if (strcmp (spname, name [mid]) <= 0) high = mid ; else low = mid + 1 ; } ------------------------------------------------------------------------------------------------------------ //快排 void QSort (int low, int high) { int l, r ; Milkcow p = cow [low] ; l = low, r = high ; while (l < r) { while (l < r && cow [r].price >= p.price) r -- ; cow [l] = cow [r] ; while (l < r && cow [l].price <= p.price) l ++ ; cow [r] = cow [l] ; } cow [l] = p ; if (l-1 > low) QSort (low, l-1) ; if (l+1 < high) QSort (l+1, high) ; } -------------------------------------------------------------------------------------------- //优化并查集 int FindSet (int i) { if (Parent [i] != i) //状态压缩 Parent [i] = FindSet (Parent [i]) ; return Parent [i] ; } void UnionSet (int a, int b) { int i, j ; i = FindSet (a) ; j = FindSet (b) ; if (i != j) if (Rank [i] > Rank [j]) //启发式合并:让深度较小的树成为深度较大的树的子树 Parent [j] = i ; else { Parent [i] = j ; if (Rank [i] == Rank [j]) Rank [j] ++ ; } } ------------------------------------------------------------------------------------------- 图论: //MST double Kruscal () { int i, k = 0 ; double s = 0 ; for (i = 0 ; i <= n ; i ++) Parent [i] = i ; for (i = 0 ; i < m && k < n-1; i ++) //m为总边数 { if (FindSet (Edge [i].a) != FindSet (Edge [i].b)) { s += Edge [i].v ; if (s > S) //是否超出范围 return 0 ; UnionSet (Edge [i].a, Edge [i].b) ; k ++ ; //记录合并的边数 } } if (k != n-1) return 0 ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值