2019-02-13-18:07:04
1.知识点合集
①:栈(Stack)是限制插入和删除只能在一端的Top进行的数据结构,栈又称LIFO(后进先出)表。
顺序栈和STLstack的一些常规操作和应用方法在之前的总结中已经讲过了,这里附上链接,栈在计算机中的重要性不言而喻,编程中的函数调用以及递归的主要实现方式都是栈。
②:队列(queue)是限制插入只能在队尾(rear)删除只能在队头(front)进行的数据结构,队列是FIFO结构。
队列和循环队列以及STLqueue的基本操作在之前已经总结过了,这里附上链接,队列的实际应用也十分的广泛,例如排队系统和计算机网络中PC机的网络设置。
③:双端队列(deque)是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。
1.定义deque
1 deque<data_type>name;//如:deque<int>z;定义一个数据类型为int的栈z。
2.添加元素
1 name.push_back(a); //在deque name的尾部添加一个元素a 2 name.push_front(a);//在deque name的头部添加一个元素a
3.删除元素
1 name.pop_back();//在deque name的尾部删除一个元素 2 name.pop_front();//在deque name的头部删除一个元素
4.元素访问
1 name.back();//返回deque name的最后一个元素 2 name.front();//返回deque name的第一个元素
5.其他操作
1 name.size();//返回deque naeme的元素个数 2 name.empty();//deque name为空时返回true
④:用到的函数
头文件:#include <cctype>
包含函数:islower();//如果该字符为小写字母则返回true
isupper();//如果该字符为大写字母则返回true
isalpha();//如果该字符为字母则返回true
isalnum();//如果该字符为数字则返回true
toupper();//如果参数是小写字母,则返回其大写,否则返回该参数
tolower();//如果参数是大写字符,则返回其小写,否则返回该参数
⑤:链表:
单向链表:当需要大量对数组元素进行移位处理时,应该选择链表存储数据,但是更为简便的一种实现方法是仍然用数组存放原有元素,但是新开辟一个数组用来存放原数组中每个元素的下一个元素的下标的值,
即用Next[i] 来表示 i 的下一个元素在原数组中的位置,这样的书写是为了尽可能避免指针带来的不必要的麻烦。
双向链表:用两个数组Right 和 Left 分别存放某个元素的左右元素在原数组中的Pos。
⑥:二叉树:
用数组实现二叉树时(从下标1开始存),最大下标为(1 << deep) 在数组实现的二叉树中,k位置的左儿子为s[2 * k],右儿子为[2 * k + 1]。
⑦:strcmp函数:
原型:extern
int
strcmp
(
const
char
*s1,
const
char
*s2);
用途:设这两个字符串为str1,str2,若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
⑧:strstr函数:
用途:strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
⑨:sscanf函数:sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
原型:int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int scanf( const char *format [,argument]... );
本章节用途:sscanf(&s[1], "%d", &v); 以s[1]为初地址的字符串中以int的形式读取一个positive number 到 v中。
①①:stringstream:
用途:一般用作字符串和数字之间的转换,需要头文件#include <sstream>。
基本操作:
1 #include <iostream> 2 #include <string> 3 #include <sstream> 4 using namespace std; 5 6 int main () { 7 8 // 字符串转数字 9 int a[10]; 10 string line = "11 22 33 44 55 66 77 88 899 1000";//string中不能存在除空格歪的其它非数字字符 11 stringstream ss; 12 ss << line;//或者直接使用stringstream ss(line); 13 int x; 14 for(int i = 0; i < 10; i ++) { 15 ss >> x; 16 a[i] = x; 17 } 18 for(int i = 0; i < 10; i ++) 19 cout << a[i] << endl; 20 stringstream sss; 21 // 将数字存入字符串 22 string s; 23 int i = 1000000; 24 sss << i;//将int输入流 25 sss >> s;//从stream中抽取前面插入int的值 26 cout << s << endl; 27 //如果要多次利用同一stringstream,需要在利用之前对它进行清空 28 string s1 = "123"; 29 stringstream ss1; 30 ss1 << s1; 31 ss1 >> x; 32 cout << x << endl; 33 ss1.clear(); 34 string s2 = "234"; 35 ss1 << s2; 36 ss1 >> x; 37 cout << x << endl; 38 //如果要清空stringstream的对象,则需要用stringstream类的内部函数s.str(""); 39 ss.str(""); 40 sss.str(""); 41 ss1.str(""); 42 return 0; 43 }
①②:ios :: sync_with_stdio(false); //关闭输入输出流与stdio的关联,这样可以使流的操作的效率和scanf不相上下,仅记使用此函数后printf和cout等不能同时使用。
①③:for(auto j : string) //相当于 for(int j = 0; j < string.size(); j ++)。
①④:memcpy(str2, str1, len); //把从str1开始的连续len个字符拷贝到str2为开始位置的字符串中。
①⑥:string.c_str(); //可以将string类型转化为仅可用C语言函数操作的字符串类型
①⑦:STL :: map容器的应用。//引用自:https://blog.csdn.net/ajianyingxiaoqinghan/article/details/78540736
原型:
1 template < class Key, // map::key_type 2 class T, // map::mapped_type 3 class Compare = less<Key>, // map::key_compare 4 class Alloc = allocator<pair<const Key,T> > // map::allocator_type 5 > class map;
属性:
关联性:关联容器中的元素的参考地址指的是其 Key 值,而不是他们在容器中的绝对地址;
有序性:容器中的元素一直按照排序方式严格排序,所有插入元素都按照该顺序排列;
映射 :每个元素中,一个 Key 值与一个映射值相关。Key 值是用来标识其主要内容是映射值的元素;
key值唯一:容器中不存在同时拥有相同 Key 值的两个元素;
参数:
Key:Key 值的类型。在 map 中的每个元素都是由其 Key 值唯一指定的。
T: 映射值的类型。在 map 中的每个元素,都存储了一些数据作为其映射值。
常用函数:
clear :
清除map中的所有元素,map.clear();
erase:
删除 map 中指定位置的元素;map.erase(map.begin());
insert :
在 map 指定位置添加 pair 类型的元素;map.insert( pair< char, int >('f', 100) );
begin, end :
map 的正向迭代器的起始位置与终点位置;
rbegin, rend :
map 的反向迭代器的起始位置与终点位置;
map.second and map.first可访问map的第一个和第二个元素。
①⑧:欧拉通路与欧拉回路:
一位大佬的博客:关于欧拉迹和欧拉闭迹。
定理:设G = (V, E) 是一个一般图,如果他的每个顶点的度数都是偶数,则G的每条边都属于一条闭迹,因而也属于一个圈。
欧拉回路:
一个无向图存在欧拉回路,当且仅当该图所有顶点度数都是偶数且该图是连通图。
一个有向图存在欧拉回路,所有顶点的入度等于出度且该图是连通图。
欧拉通路:
一个无向图存在欧拉通路,当且仅当该图只存在2个读书为奇数的顶点且该图是连通图。
一个有向图存在欧拉通路,当且仅当该图只存在一个起点(出度比入度大一),一个终点(入度比出度大一),其余顶点的入度等于出度。
构造欧拉回路的Fleury算法:从连通多重图的任意一个顶点开始,连续的选择边来形成一条回路。一旦选择了一条边就删除这条边。连续的选择边,使得每条边从上一条边结束的地方开始,
而且使得这条边不是一条割边,除非别无选择。
利用DFS构造欧拉回路和欧拉通路:
1 void euler(int u) {//构造欧拉迹需从道路的起点出发,有向图则只需删除访问的那条边 2 for(int v = 0; v < n; v ++) 3 if(G[u][v]) { 4 G[u][v] --; 5 G[v][u] --; 6 euler(v); 7 stack.push(u, v);//将存(u,v)的结构体入栈 8 } 9 }
emmm现在能力有限,后续会补充并查集判断回路和混合图的欧拉闭迹和欧拉迹。
2.课后习题
例6 - 2:UVA514,题目链接
题目概述:本题是典型的火车出站问题,火车由多个车厢组成,每个车厢都可以单独进出站,给出进站序列,问某一序列是否满足正常的出站序列。
算法思路:用栈的进出栈模拟火车的进出站。
参考代码:
//这里是我第一次写的代码,不知道为何一直WA,emm这样做看起来确实麻烦了一点,但是实在找不出来Bug。
//错误代码,哪位大佬帮忙找找Bug,emm~ #include <iostream> #include <stack> using namespace std; const int Maxn = 1010; int a[Maxn]; int b[Maxn]; int c[Maxn]; int main () { int n; while(cin >> n && n) { for(int i = 1; i <= n; i ++) a[i] = i; while(cin >> b[1] && b[1]) { for(int i = 2; i <= n; i ++) cin >> b[i]; stack <int> s; int front = 1; for(int i = front; i <= n; i ++) { for(int j = front; j <= b[i]; j ++) s.push(a[j]); while(!s.empty()) { c[front ++] = s.top(); s.pop(); } } for(int i = 1; i <= n; i ++) { if(b[i] != c[i]) { cout << "No" << endl; break; } if(i == n) cout << "Yes" << endl; } } cout << endl; } return 0; }
//参考Lrj思路之后的代码(emm~不得不说刘大佬还是真的强(小声bb:我是真的菜))
1 #include <iostream> 2 #include <stack> 3 using namespace std; 4 5 const int Maxn = 1010; 6 int target[Maxn]; 7 8 int main () { 9 int n; 10 while(cin >> n && n) { 11 stack <int> s; 12 while(cin >> target[1] && target[1]) { 13 for(int i = 2; i <= n; i ++) 14 cin >> target[i]; 15 int A = 1, B = 1; 16 bool flag = true; 17 while(B <= n) { //如果驶向B的车个数大于n,则循环结束。 18 //如果驶向C的车等于驶向B的车的序列号,直接该将车驶进B
19 if(A == target[B]) { A ++; B ++; }
20 //否则,则判断栈顶的(即在C最上面)车是否等于驶向B的车 21 else if(!s.empty() && s.top() == target[B]) { s.pop(); B ++; } 22 //将车驶进C 23 else if(A <= n) { s.push(A ++); } 24 //如果车全部都驶入C,循环还没有结束,意味着所给的target顺序不能实现 25 else { flag = false; break; } 26 } 27 printf("%s ", flag ? "Yes" : "No"); 28 } 29 cout << endl; 30 } 31 return 0; 32 }
例题6-3:UVA442,题目链接
题目概述:
无
算法思路:
遍历表达式,当遇到大写字母时将其对应的Matrix进栈,当遇到 ‘ ) '时取栈顶两个元素进行计算,并在计算结束之后将他们所组成的新Matrix进栈。遍历全部字符串之后输出结果即可。
参考代码:
1 #include <iostream> 2 #include <stack> 3 #include <cctype> 4 using namespace std; 5 6 typedef struct { 7 char c; 8 int rows, columns; 9 } Matrix; 10 11 const int Maxn = 27; 12 Matrix matrix[Maxn]; 13 stack <Matrix> mat; 14 int main () { 15 int n; 16 cin >> n; 17 for(int i = 0; i < n; i ++) 18 cin >>matrix[i].c >> matrix[i].rows >> matrix[i].columns; 19 string s; 20 char ch; 21 bool flag = true; 22 Matrix med1, med2; 23 int left, right, ans; 24 while(cin >>s) { 25 ans = 0; 26 flag =true; 27 for(unsigned int i = 0; i < s.length(); i ++) { 28 if(isupper(s[i])) 29 mat.push(matrix[s[i] - 'A']); 30 if(s[i] == ')') { 31 med1 = mat.top(); 32 mat.pop(); 33 med2 = mat.top(); 34 mat.pop(); 35 if(med1.rows == med2.columns) { 36 ans += med1.rows * med2.rows * med1.columns; 37 med1.rows = med2.rows; 38 mat.push(med1); 39 } 40 else { 41 flag = false; 42 break; 43 } 44 } 45 } 46 if(flag) 47 cout << ans << endl; 48 else 49 cout << "error" << endl; 50 } 51 return 0; 52 }
例6-4 :UVA11988,题目链接
题目概述:
无
算法思路:
当需要大量对数组元素进行移位处理时,应该选择链表存储数据,但是更为简便的一种实现方法是仍然用数组存放原有元素,但是新开辟一个数组用来存放原数组中每个元素的下一个元素的下标的值,
即用Next[i] 来表示 i 的下一个元素在原数组中的位置,这样的书写是为了尽可能避免指针带来的不必要的麻烦。
参考代码:
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 using namespace std; 5 6 const int maxn = 100005; 7 int cur, last;//记录光标位置和链表中最后一个元素的位置 8 int Next[maxn];//用Next[i]存储i位置元素的下一个元素的位置 9 char s[maxn]; 10 11 int main () { 12 while(scanf("%s", s + 1) == 1) { 13 int n = strlen(s + 1);//从数组的第一位开始保存字符串 14 last = cur = 0;//先初始化当前指针方向和Next数组的最后一个指针都指向0 15 Next[0] = 0; 16 for(int i = 1; i <= n; i ++) {//对于每个字符进行单独操作 17 char ch = s[i];//保存s[i]的值,避免多次重复运算 18 if(ch == '[') cur = 0;//如果字符代表Home则将当前光标(cur)移至‘0’ 19 else if (ch == ']') cur = last;//如果字符代表End则将当前光标(cur)移至last 20 else { 21 Next[i] = Next[cur];//让当前元素位置的Next值等于光标所在位置的Next(即将该元素的位置插入到Next数组中) 22 Next[cur] = i;//将当前元素的坐标保存到光标所在位置的Next值中 23 if(cur == last) last = i;//当发现cur == last时更新last 24 cur = i;//每次将当前光标回到上一个被标记的地方 25 } 26 } 27 for(int i = Next[0]; i != 0; i = Next[i]) 28 printf("%c", s[i]); 29 printf(" "); 30 } 31 return 0; 32 }
例6-6UVA-679,原题链接
题目概述:
无
算法思路:
水题,用s[k] = !s[k]改变开关状态,小球下落选取结点时选用三元运算符比if else更高效。
参考代码:
TLE代码,模拟小球下落会造成巨大的时间浪费和空间浪费。
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 const int Maxd = 20; 6 bool s[1 << Maxd];//用于存储二叉树根结点状态的数组,最大下标为pow(2, Maxd) - 1 7 //在数组实现的二叉树中,k位置的左儿子为s[2 * k],右儿子为[2 * k + 1] 8 9 int main () { 10 int D, I, k, max; 11 while(cin >> D >> I) { 12 max = (1 << D) - 1; 13 memset(s, false, sizeof(s)); 14 for(int i = 0; i < I; i ++) { 15 k = 1; 16 while(true) { 17 s[k] = !s[k]; 18 k = s[k] ? k * 2 : k * 2 + 1; 19 if(k > max) 20 break; 21 } 22 } 23 cout << k / 2 << endl; 24 } 25 return 0; 26 }
事实上,每次下落只需要判断小球编号的奇偶性,如果为奇,他是往左走的第(i + 1)个小球,如果为偶,他是往右走的第i / 2个小球,则可以得出下面的代码。
1 #include <iostream> 2 using namespace std; 3 4 int main () { 5 int n, D, I; 6 while(cin >> n && n != -1) { 7 while(n --) { 8 cin >> D >> I; 9 int k = 1; 10 for(int i = 2; i < D; i ++) { 11 /* 12 //如果有偶数颗球,则第一颗落在编号为2的结点上, 13 假设有I颗球从1落下,则相当于有(I + 1) / 2颗从2号落下, 14 有I / 2颗从3号落下,以此类推。 15 */ 16 if(I % 2) { k = k * 2; I = (I + 1) / 2; 17 else { k = k * 2 + 1; I /= 2; } 18 } 19 cout << k << endl; 20 } 21 } 22 }
例6-7:UVA122,原题链接
题目概述:
路径建树,树的层序遍历。
算法思路:
根据输入直接建树,遍历之后存数组,然后直接输出。
参考代码:
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 8 typedef struct node{ 9 bool is_value; 10 int weight; 11 struct node *left, *right; 12 }Node, *PNode; 13 14 int v; 15 const int Maxn = 300; 16 char s[Maxn];//用来存储读入的字符串 17 PNode root; 18 bool failed; 19 vector <int> ans; 20 21 bool read_input(); 22 PNode Newnode(); 23 void AddNode(PNode &root, int v, char *s); 24 bool bfs(vector <int> &ans); 25 void remove_tree(PNode u); 26 void Print(vector <int> ans); 27 28 int main () { 29 while(read_input()) { 30 if(! bfs(ans) || failed) 31 cout << "not complete" << endl; 32 else 33 Print(ans); 34 35 } 36 return 0; 37 } 38 39 bool read_input() { 40 failed = false; 41 if(root) 42 remove_tree(root); 43 root = Newnode(); 44 while(true) { 45 if(scanf("%s", s) != 1) return false; 46 if(!strcmp(s, "()")) break; 47 sscanf(&s[1], "%d", &v);//在指定地址开始的一片连续空间中读取指定类型的字符 48 AddNode(root, v, strchr(s, ',') + 1);//strchr函数为在指定地址开始的一片连续空间中寻找某个字符,返回该字符的地址 49 } 50 return true; 51 } 52 53 PNode Newnode() { 54 PNode root = new Node;//为结点开辟空间,并进行相应的初始化 55 root -> is_value = false; 56 root -> left = NULL; 57 root -> right = NULL; 58 return root; 59 } 60 61 void AddNode(PNode &root, int v, char *s) { 62 int len = strlen(s); 63 PNode u = root; 64 for(int i = 0; i < len; i ++) 65 if(s[i] == 'L') { 66 if(u -> left == NULL) u -> left = Newnode(); 67 u = u -> left; 68 } 69 else if(s[i] == 'R') { 70 if(u -> right == NULL) u -> right = Newnode(); 71 u = u -> right; 72 } 73 if(u -> is_value) failed = true; 74 u -> weight = v; 75 u -> is_value = true; 76 } 77 78 bool bfs(vector <int> &ans) { 79 queue <PNode> q; 80 ans.clear();//清空之前的ans 81 q.push(root); 82 while(! q.empty()) { 83 PNode u = q.front(); 84 q.pop(); 85 if(! u -> is_value) return false; 86 ans.push_back(u -> weight); 87 if(u -> left) q.push(u -> left); 88 if(u -> right) q.push(u -> right); 89 } 90 return true; 91 } 92 93 void remove_tree(Node *u) { 94 if(u == NULL) return; 95 remove_tree(u -> left); 96 remove_tree(u -> right); 97 delete u; 98 } 99 100 void Print(vector <int> ans) { 101 unsigned int len = ans.size(); 102 for(int i = 0; i < len; i ++) { 103 if(i) 104 cout << ' '; 105 cout << ans[i]; 106 } 107 cout << endl; 108 }
例6-8:UVA548,原题链接
题目概述:
无
算法思路:
根据输入的中序和后续遍历重构出原二叉树,遍历二叉树的结点,求出权值的最小值。
参考代码:
1 #include <iostream> 2 #include <string> 3 #include <sstream> 4 using namespace std; 5 6 const int Maxn = 10000 + 10; 7 int in_order[Maxn], post_order[Maxn], leftchild[Maxn], rightchild[Maxn]; 8 int best, best_sum, n; 9 10 bool read_input(int *a); 11 int build(int L1, int R1, int L2, int R2); 12 void dfs(int u, int sum); 13 14 int main () { 15 while(read_input(in_order)) { 16 read_input(post_order); 17 build(0, n - 1, 0, n - 1); 18 best_sum = 1000000000; 19 dfs(post_order[n - 1], 0); 20 cout << best << endl; 21 } 22 return 0; 23 } 24 bool read_input(int *a) { 25 string line; 26 if(!getline(cin, line)) return false; 27 stringstream ss(line); 28 int x; 29 n = 0; 30 while(ss >> x) a[n ++] = x; 31 return n > 0; 32 } 33 34 //把in_order[L1....R1]和post_order[L2...R2]建成一棵树,返回树根 35 int build(int L1, int R1, int L2, int R2) { 36 if(L1 > R1) return 0;//空树 37 int root = post_order[R2]; 38 int p = L1; 39 while(in_order[p] != root) p ++; 40 int cnt = p - L1;//左子树结点的个数 41 leftchild[root] = build(L1, p - 1, L2, L2 + cnt - 1); 42 rightchild[root] = build(p + 1, R1, L2 + cnt, R2 - 1); 43 return root; 44 } 45 46 void dfs(int u, int sum) { 47 sum += u; 48 if(!leftchild[u] && !rightchild[u]) {//叶子结点 49 if(sum < best_sum || (sum == best_sum && u < best)) { 50 best = u; 51 best_sum = sum; 52 } 53 } 54 if(leftchild[u]) dfs(leftchild[u], sum); 55 if(rightchild[u]) dfs(rightchild[u], sum); 56 }
例6-9:UVA839,原题链接
题目概述:
无
算法思路:
回溯思想写代码,只要每一颗二叉树的左右子树的左右平衡则最后的二叉树就是平衡的。
参考代码:
1 #include <iostream> 2 using namespace std; 3 4 bool solve(int &W) { 5 int W1, W2, D1, D2; 6 bool b1 = true, b2 = true; 7 cin >> W1 >> D1 >> W2 >> D2; 8 if(!W1) b1 = solve(W1); 9 if(!W2) b2 = solve(W2); 10 W = W1 + W2; 11 return b1 && b2 && (W1 * D1 == W2 * D2); 12 } 13 14 int main () { 15 int T, W; 16 cin >> T; 17 while(T --) { 18 if(solve(W)) cout << "YES "; else cout << "NO "; 19 if(T) cout << endl; 20 } 21 return 0; 22 }
例6-10:UVA699,原题链接
题目概述:
无
算法思路:
由于要按照二叉树的水平位置从小到大输出每层的权值之和,则需要建立一个数组对每层说的权值之和进行存储,每次选取数组的中心作为树根root的位置,对其左右两个位置进行递归建树并且统计每层的权值之和,之后按照
从左到右的顺序进行权值之和的输出。
参考代码:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 const int maxn = 200; 6 int sum[maxn]; 7 8 void build(int p) { 9 int v; cin >> v; 10 if(v == -1) return; 11 sum[p] +=v; 12 build(p - 1); build(p + 1); 13 } 14 15 bool init() { 16 int v; 17 cin >> v; 18 if(v == -1) return false; 19 memset(sum, 0, sizeof(sum)); 20 int pos = maxn / 2; 21 sum[pos] = v; 22 build(pos - 1); build(pos + 1); 23 return true; 24 } 25 26 int main () { 27 int Kase = 0; 28 while(init()) { 29 int p = 0; 30 while(sum[p] == 0) p ++; 31 cout << "Case " << ++ Kase << ": " << sum[p ++]; 32 while(sum[p] != 0) cout << " " << sum[p ++]; 33 cout << endl << endl; 34 } 35 return 0; 36 }
例题6-11:UVA297,原题链接
题目概述:
无
算法思路:
由于P代表中间结点,e和f代表叶子结点,则可知仅由Pre-Order就可以确定一颗四分树。只需要编写出来一个画出来的过程,边画边统计即可。
参考代码:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 const int len = 32; 6 const int maxn = 1024 + 10; 7 char s[maxn]; 8 int buf[len][len], cnt; 9 10 void draw(const char *s, int &p, int r, int c, int w) { 11 char ch = s[p ++]; 12 if(ch == 'p') { 13 draw(s, p, r, c + w / 2, w / 2); 14 draw(s, p, r , c, w / 2); 15 draw(s, p, r + w / 2, c, w / 2); 16 draw(s, p, r + w / 2, c + w / 2, w / 2); 17 } else if(ch == 'f') { 18 for(int i = r; i < r + w; i ++) 19 for(int j = c; j < c + w; j ++) 20 if(buf[i][j] == 0) { 21 buf[i][j] = 1; 22 cnt ++; 23 } 24 } 25 } 26 27 int main () { 28 int T; 29 cin >> T; 30 while(T --) { 31 memset(buf, 0, sizeof(buf)); 32 cnt = 0; 33 for(int i = 0; i < 2; i ++) { 34 cin >> s; 35 int p = 0; 36 draw(s, p, 0, 0, len); 37 } 38 printf("There are %d black pixels. ", cnt); 39 } 40 return 0; 41 }
例6-12:UVA572, 原题链接
题目概述:
无
算法思路:
连通块题目通过DFS实现较为容易,遍历图中的每个格子,如果是' @ '且没有访问过则对她周边的元素进行访问并标记,对它周围的格子进行递归式的遍历,确定图中的每一个格子都被访问到。
参考代码:
#include <cstdio> #include <cstring> using namespace std; const int Maxn = 100 + 10; char pic[Maxn][Maxn]; int m, n, idx[Maxn][Maxn]; void dfs(int r, int c, int id) { if(r < 0 || r >= m || c < 0 || c >= n) return;//坐标越界 if(idx[r][c] > 0 || pic[r][c] != '@') return;//不是'@'或者已经访问过的情况 idx[r][c] = id; for(int dr = -1; dr <= 1; dr ++) for(int dc = -1; dc <= 1; dc ++) if(dr != 0 || dc != 0) dfs(r + dr, c + dc, id); } int main () { while(~scanf("%d %d", &m, &n) && m && n) { for(int i = 0; i < m; i ++) scanf("%s",pic[i]); memset(idx, 0, sizeof(idx)); int cnt = 0; for(int i = 0; i < m; i ++) for(int j = 0; j < n; j ++) if(idx[i][j] == 0 && pic[i][j] == '@') dfs(i, j, ++cnt); printf("%d ", cnt); } return 0; }
例6-13:UVA1103, 原题链接
题目概述:
无
算法思路:
先将输入转为2进制存入数组,对该数组进行初次试探 (将所有黑色连通块单独分割出来) ,接着对于每个连通块的白色区域进行相同的探测,找出黑色连通块中白色连通区域的个数,利用map容器的特性,
自动将对应的字符字典序排序,紧接着输出即可。
参考代码:
1 /* 2 算法思路:先将输入转为2进制存入数组,对该数组进行初次试探(将所有连通块单独分割出来) 3 接着对于每个连通块的白色区域进行相同的探测,找出黑色连通块中白色连通区域的个数,利用 4 map容器的特性,自动将对应的字符字典序排序,紧接着输出即可。 5 6 */ 7 8 #include <bits/stdc++.h> 9 using namespace std; 10 11 const string dict[16] = { 12 "0000", "0001", "0010", "0011", 13 "0100", "0101", "0110", "0111", 14 "1000", "1001", "1010", "1011", 15 "1100", "1101", "1110", "1111", 16 }; 17 const int dir[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; 18 const char alp[8] = { 'W', 'A', 'K', 'J', 'S', 'D' };//按照白色连通块的个数递增序存入对应的字符 19 map<char, int> cnt; 20 char tab[256][256]; 21 int H, W, kase, cur; 22 23 bool isin(const int r, const int c) {//判断是否越界 24 return r >= 0 && r <= H + 1 && c >= 0 && c <= W + 1; 25 } 26 27 void dfs1(const int r, const int c) {//如果当前位置为白色块则将其标记为边界,并对其上下左右进行相同的探测 28 if(! isin(r, c) || tab[r][c] != '0') return; 29 tab[r][c] = '-'; 30 for(int i = 0; i < 4; i ++) 31 dfs1(r + dir[i][0], c + dir[i][1]); 32 } 33 34 void dfs2(const int r, const int c) { 35 if(! isin(r, c) || tab[r][c] != '1') return; 36 tab[r][c] = '-';//将已经寻找过的连通块标记为边界,并探索它的上下左右, 37 //如果找到白色连通块则将白色连通块附近的白色连通块都设置为'-',如果找到的是黑色字符则进行递归探索 38 for(int i = 0; i < 4; i ++) { 39 int r1 = r + dir[i][0], c1 = c + dir[i][1]; 40 if(tab[r1][c1] == '0') 41 ++cur, dfs1(r1, c1); 42 else dfs2(r1, c1); 43 } 44 } 45 46 int main () { 47 ios :: sync_with_stdio(false);//关闭输入输出流与stdio的关联,这样可以使流的操作的效率和stdio不相上下 48 while(memset(tab, '0', sizeof(tab)), cnt.clear(), cin >> H >> W, H || W) { 49 W *= 4; 50 for(int i = 1; i <= H; i ++) {//16进制转2进制,并且从第一列开始存储 51 string line, res; 52 cin >> line; 53 for(auto j : line) res +=dict[j >= 'a' ? (j - 'a' + 10) : (j - '0')];//对于line中的每一个元素对其进行操作,相当于for(int j = 0; j < line.length(); j ++) 54 memcpy(tab[i] + 1, res.c_str(), W);//string.c_str可以将string类型转化为仅可用C语言函数操作的字符串类型 55 } 56 dfs1(0, 0);//执行结束后所有的连通块将被边界字符'-'包住 57 for(int i = 1; i <= H; i ++) { 58 for(int j = 1; j <= W; j ++) { 59 if(tab[i][j] != '1') continue;//对于每个连通块,先初始化其中的白色连通块数目为0 60 cur = 0; 61 dfs2(i, j); 62 cnt[alp[cur]] ++; 63 } 64 } 65 cout << "Case " << ++kase << ": "; 66 for(auto i : cnt) while(i.second --) cout << i.first; 67 cout << endl; 68 } 69 return 0; 70 }
例6-14 :UVA816,原题链接
题目概述:
无
参考代码:
1 /* 2 算法思路: 3 ①:首先将整个maza读入,并用has_edge数组记录每个每个方向的可移动位置。 4 ②:接着对maza的第一个元素入队列,对队列中的元素进行操作,如果队首元素存在则判断该点该方向下是否可向其它方向移动并判断移动之后是否还在在迷宫边界范围内且之前未探索, 5 如果满足这三个条件则将移动后的点入队列等待探索,直至将所有元素探索完毕。 6 ③:逆序打印路径即可。 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 11 struct Node{ 12 int r, c, dir;// 表示结点的横纵坐标和方向 13 Node(int r = 0, int c = 0, int dir = 0) :r(r), c(c), dir(dir) {};//对结构体元素进行初始化 14 }; 15 const int maxn = 10; 16 const char *dirs = "NESW"; 17 const char *turns = "FLR"; 18 const int dr[] = {-1, 0, 1, 0}; 19 const int dc[] = {0, 1, 0, -1}; 20 int has_edge[maxn][maxn][4][3];//保存(r, c, dir)状态下的可移动方向 21 int d[maxn][maxn][4];//存储初始状态到(r, c, dir)的最短路长度 22 Node p[maxn][maxn][4];//同时用p[r][c][dir]保存了状态(r, c, dir)在BFS树中的父节点 23 int r0, c0, dir, r1, c1, r2, c2; 24 char name[99]; 25 26 int dir_id(char c) { 27 return strchr(dirs, c) - dirs; 28 } 29 30 int turn_id(char c) { 31 return strchr(turns, c) - turns; 32 } 33 34 Node walk(const Node &u, int turn) { 35 int dir = u.dir; 36 if(turn == 1) dir = (dir + 3) % 4;//逆时针 37 if(turn == 2) dir = (dir + 1) % 4;//顺时针 38 return Node(u.r + dr[dir], u.c + dc[dir], dir); 39 } 40 41 bool inside(int r, int c) { 42 return r >= 1 && r <= 9 && c >= 1 && c <=9; 43 } 44 45 bool read_case () { 46 char s[99], s2[99]; 47 scanf("%s", name); 48 if(!strcmp(name, "END")) return false; 49 scanf("%d %d %s %d %d", &r0, &c0, s2, &r2, &c2); 50 cout << name << endl; 51 dir = dir_id(s2[0]); 52 r1 = r0 + dr[dir]; 53 c1 = c0 + dc[dir]; 54 memset(has_edge, 0, sizeof(has_edge)); 55 while(true) { 56 int r, c; 57 scanf("%d", &r); 58 if(r == 0) break; 59 scanf("%d", &c); 60 while(~scanf("%s", s) && s[0] != '*') { 61 for(int i = 1; i < strlen(s); i ++) 62 has_edge[r][c][dir_id(s[0])][turn_id(s[i])] = 1; 63 } 64 } 65 return true; 66 } 67 68 void print_ans(Node u) { 69 //从目标节点逆序输出 70 vector <Node> nodes; 71 while(true) { 72 nodes.push_back(u); 73 if(d[u.r][u.c][u.dir] == 0) break; 74 u = p[u.r][u.c][u.dir]; 75 } 76 nodes.push_back(Node(r0, c0, dir)); 77 //打印解,每行10个 78 int cnt = 0; 79 for(int i = nodes.size() - 1; i >= 0; i --) { 80 if(cnt % 10 == 0) printf(" "); 81 printf(" (%d,%d)", nodes[i].r, nodes[i].c); 82 if(++ cnt % 10 == 0) printf(" "); 83 } 84 if(nodes.size() % 10 != 0) printf(" "); 85 } 86 87 void solve () { 88 queue <Node> q; 89 memset(d, -1, sizeof(d)); 90 Node u(r1, c1, dir); 91 d[u.r][u.c][u.dir] = 0; 92 q.push(u); 93 while(!q.empty()) { 94 Node u = q.front(); 95 q.pop(); 96 if(u.r == r2 && u.c == c2) { 97 print_ans(u); 98 return; 99 } 100 for(int i = 0; i < 3; i ++) { 101 Node v = walk(u, i); 102 if(has_edge[u.r][u.c][u.dir][i] && inside(v.r, v.c) && d[v.r][v.c][v.dir] < 0) { 103 d[v.r][v.c][v.dir] = d[u.r][u.c][u.dir] + 1; 104 p[v.r][v.c][v.dir] = u; 105 q.push(v); 106 } 107 } 108 } 109 printf(" No Solution Possible "); 110 } 111 112 int main () { 113 while(read_case()) { 114 solve(); 115 } 116 return 0; 117 }
例6-15:UVA-10305,原题链接
题目概述:
给定n个Tasks,并给出m个完成 Task j 之前需完成 Task i 的序列 (i, j) ,让你求出一个做任务的先后序列。
参考代码:
dfs实现拓扑排序
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 100 + 5; 5 int n, m, t, a, b; 6 int G[maxn][maxn], res[maxn], c[maxn]; 7 8 bool dfs(int u) { 9 c[u] = -1;//表示u正在访问 10 for(int v = 0; v < n; v ++) { 11 if(G[u][v]) 12 if(c[v] < 0) return false;//存在有向环,失败 13 else if(!c[v] && !dfs(v)) return false;//如果发现v未访问但是v内部存在有向环则false 14 } 15 c[u] = 1; 16 res[-- t] = u;//每次将u存储至拓扑序的头部 17 return true; 18 } 19 20 bool topsort() { 21 t = n; 22 memset(c, 0, sizeof(c)); 23 for(int u = 0; u < n; u ++) { 24 if(!c[u])//逐行进行搜查,如果发现第u行未访问则对这行进行访问 25 if(!dfs(u)) 26 return false; 27 } 28 return true; 29 } 30 31 int main () { 32 ios::sync_with_stdio(false); 33 while(cin >> n >> m &&(n || m)) { 34 memset(G, 0, sizeof(G)); 35 for(int i = 0; i < m; i ++) { 36 cin >> a >> b; 37 G[a - 1][b - 1] = 1;//将先后关系用邻接矩阵的方式存储起来 38 } 39 topsort();//对图进行拓扑排序 40 for(int i = 0; i < n; i ++) 41 cout << res[i] + 1 << " "; 42 cout << endl; 43 } 44 return 0; 45 }
队列实现:
1 /* 2 算法思想: 3 先将每个入度为0的结点入队,将其放到拓扑序的尾部,对每个队首元素,将它指向的结点的入度减一, 4 如果此时改结点入度为0则入队,一直进行此操作,队列为空时排序结束。 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 int m, n, a, b, t; 9 const int maxn = 100 + 5; 10 int G[maxn][maxn], res[maxn], indegree[maxn]; 11 12 void topsort() { 13 t = 0; 14 queue <int> q; 15 for(int i = 0; i < n; i ++) 16 if(indegree[i] == 0) 17 q.push(i); 18 while(!q.empty()) { 19 int p = q.front(); 20 q.pop(); 21 res[t ++] = p; 22 for(int i = 0; i < n; i ++) 23 if(G[p][i] != 0) 24 if(-- indegree[i] == 0) 25 q.push(i); 26 } 27 for(int i = 0; i < n; i ++) 28 cout << res[i] + 1 << " "; 29 cout << endl; 30 } 31 32 int main () { 33 ios::sync_with_stdio(false); 34 while(cin >> n >> m && (m || n)) { 35 memset(G, 0, sizeof(G)); 36 memset(indegree, 0, sizeof(indegree)); 37 for(int i = 0; i < m; i ++) { 38 cin >> a >> b; 39 G[a - 1][b - 1] = 1; 40 indegree[b - 1] ++; 41 } 42 topsort(); 43 } 44 return 0; 45 }
例6-16:UVA-10129,原题链接
题目概述:
无
用DFS判断图是否联通,再判断出入度是否满足形成欧拉迹的条件即可。
参考代码:·
/* 算法思路: 用DFS判断图是否联通,再判断出入度是否满足形成欧拉迹的条件即可。 */ #include <bits/stdc++.h> using namespace std; const int maxn = 100000 + 5; const int nmax = 27; int n, len, Begin, End; int G[nmax][nmax], indegree[nmax], outdegree[nmax]; bool flag; void iseuler() { for(int i = 0; i < nmax; i ++) { if(indegree[i] == outdegree[i]) continue; else if(indegree[i] - outdegree[i] == 1 && End == -1) End = i; else if(outdegree[i] - indegree[i] == 1 && Begin == -1) Begin = i; else flag = false; } } void traverse(int u) { for(int v = 0; v < nmax; v ++) { if(G[u][v]) { G[u][v] = 0; traverse(v); } } } int main () { // freopen("output.txt", "w", stdout); ios :: sync_with_stdio(false); int T; cin >> T; while(T --) { flag = true; Begin = End = -1; memset(G, 0, sizeof(G)); memset(indegree, 0, sizeof(indegree)); memset(outdegree, 0, sizeof(outdegree)); cin >> n; while(n --) { string s; cin >> s; len = s.length(); G[s[0] - 'a'][s[len - 1] - 'a'] = 1; outdegree[s[0] - 'a'] ++; indegree[s[len - 1] - 'a'] ++; } iseuler(); if(flag) if(Begin != -1) traverse(Begin); else for(int u = 0; u < nmax; u ++) if(outdegree[u]) { traverse(u); break; } for(int u = 0; u < nmax; u ++) for(int v = 0; v < nmax; v ++) if(G[u][v]) flag =false; if(flag) cout << "Ordering is possible." << endl; else cout << "The door cannot be opened." << endl; } // fclose(stdout); return 0; }