2020暑期实训算法温故知新
这次接触算法应该是我学习代码以来最多的一次,而且写完这些算法的时间很短,根本来不及进行整理和归纳,因此,我认为进行这样一次温故知新是非常有必要的,一方面,我可以把一些我认为能够通过c++ 的STL优化的算法重写一遍(这次实训最坑的一点就是只能用C,什么都要自己造,而我造的东西移植性又不好,性能也不咋地,哭了~~),另一方面,我可以对当时一些不是很清楚的算法做一些总结和归类,这也算是我的第一个小题库。
龙虎斗 枚举
轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n个兵营(自左至右编号 1~ n),相邻编号的兵营之间相隔 1 厘米,即棋盘为长度为 n - 1 厘米的线段。i 号兵营里有 ci 位工兵。
下面图 11 为 n = 6 的示例:
图 11. n = 6的示例
轩轩在左侧,代表 “龙”;凯凯在右侧,代表 “虎”。他们以 m号兵营作为分界,靠左的工兵属于龙势力,靠右的工兵属于虎势力,而 第 m 号兵营中的工兵很纠结,他们不属于任何一方 。
一个兵营的气势为:该兵营中的工兵数 × 该兵营到 m 号兵营的距离;参与游戏一方的势力定义为:属于这一方所有兵营的气势之和。
下面图 22 为 n = 6, m = 4的示例,其中红色为龙方,黄色为虎方:
图 22. n = 6, m = 4 的示例
游戏过程中,某一刻天降神兵,共有 s1 位工兵突然出现在了 p1 号兵营。作为轩轩和凯凯的朋友,你知道如果龙虎双方气势差距太悬殊,轩轩和凯凯就不愿意继续玩下去了。为了让游戏继续,你需要选择一个兵营p2,并将你手里的s2 位工兵 全部 派往兵营 p2,使得双方气势差距尽可能小。
注意:你手中的工兵落在哪个兵营,就和该兵营中其他工兵有相同的势力归属(如果落在 m号兵营,则不属于任何势力)。
输入格式 输入文件的第一行包含一个正整数 n,代表兵营的数量。
接下来的一行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i个正整数代表编号为 i的兵营中起始时的工兵数量 ci。
接下来的一行包含四个正整数,相邻两数间以一个空格分隔,分别代表 m,p1,s1,s2。
输出格式 输出文件有一行,包含一个正整数,即 p2,表示你选择的兵营编号。
如果存在多个编号同时满足最优,取最小的编号。
数据范围 1 < m < n, 1≤ p1 ≤n。
对于 20% 的数据,n*=3,m =2,*ci=1,s1,s2≤100。
另有 20% 的数据,n≤10,p1=m,ci=1,s1,s2≤100。
对于 60% 的数据,n≤100,ci=1,s1,s2≤100。
对于 80% 的数据,n≤100,ci,s1,s2≤100。
对于100% 的数据,n≤$10^5$,ci,s1,s2≤$10^9$。
样例说明 样例 1
双方以 m=4号兵营分界,有s1=5 位工兵突然出现在 p1=6 号兵营。
龙方的气势为:
2 x (4 - 1) + 3 x (4 - 2) + 2 x (4 - 3) = 14
虎方的气势为:
2 x (5 - 4) + (3 + 5) x (6 - 4) = 18
当你将手中的s2=2 位工兵派往p2=2 号兵营时,龙方的气势变为:
14 + 2 × ( 4 − 2 ) = 18
此时双方气势相等。
样例 2
双方以 m = 5 号兵营分界,有s1=1 位工兵突然出现在 p1=4 号兵营。
龙方的气势为:
1 × ( 5−1 ) + 1 × ( 5−2 ) + 1 × ( 5−3 ) + ( 1+1 ) × ( 5−4 ) = 11
虎方的气势为:
16 × ( 6 −5 ) = 16
当你将手中的 s2=1 位工兵派往 p 2=1 号兵营时,龙方的气势变为:
11+1×(5−1)=15
此时可以使双方气势的差距最小。
解题思路 现在看来,当时做题可能是被题目的长度给吓到了,如果时间的限制不太严的话,直接枚举每一个值,分别计算气势差距就OK了,最后时间复杂度在O(n),我估计当时应该是被时间给卡住了,但直接枚举比较不费脑子,所以也懒得想什么奇淫技巧了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std;int n;int m;int p1, s1;int p2, s2;int lpower = 0 , rpower = 0 ;int main () { int min = 0x3f3f3f3f ; int gap; cin >> n; int i ,j; vector<int > build; build.push_back (0 ); for (i = 0 ;i < n;i++){ cin >> m; build.push_back (m); } cin >> m >> p1 >> s1 >> s2; build[p1] += s1; for (i = 1 ;i < m ;i++){ lpower += build[i] * (m - i); } for (i = m + 1 ;i <= n;i++){ rpower += build[i] * (i - m); } for (i = 1 ;i <= n;i++){ int tmp; if (i < m){ tmp = lpower; lpower += s2 * (m - i); gap = abs (lpower - rpower); lpower = tmp; } else if (i > m){ tmp = rpower; rpower += s2 * (i - m); gap = abs (lpower - rpower); rpower = tmp; } else { gap = abs (lpower - rpower); } if (gap < min){ min = gap; p2 = i; } } cout << p2 << endl; return 0 ; }
大整数加法 高精度
求两个不超过 200200 位的非负整数的和。
输入格式 有两行,每行是一个不超过 200200 位的非负整数,可能有多余的前导 00。
输出格式 一行,即相加后的结果。结果里不能有多余的前导 00,即如果结果是 342342,那么就不能输出为 03420342。
解题思路 使用dequeue能够快速头插元素对这题帮助极大!毫无疑问,如果只能使用C语言来解析这题,第一层麻烦就是静态数组,如果正向做加法,会把数组颠三倒四好多次,但如果逆向做加法,由于不清楚各个数组的长度和是否有最终进位,因此空间十分不好申请,还有一点就是,如果不让两个数组长度相同(其中一个用0补充),那么判断条件会增添许多麻烦,如果让其中一个填充0,那头插又会耗费数组大量时间。
而有了deque,这一切都不是问题,我们可以像做竖式加法一样去思考这个大整数加法。将两个字符串转化一下读到deque中,用头插补0,然后申请一个逆向的迭代器就可以愉快地做加法啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> using namespace std;int main () { string str1, str2; cin >> str1 >> str2; int len1 = str1.length (); int len2 = str2.length (); deque<int > qu1, qu2; string::reverse_iterator it; for (it = str1.rbegin (); it != str1.rend ();it++){ qu1.push_front (int ((*it) - '0' )); } for (it = str2.rbegin (); it != str2.rend ();it++){ qu2.push_front (int ((*it) - '0' )); } int gap; if (len1 > len2){ gap = len1 - len2; while (gap){ qu2.push_front (0 ); gap--; } } else { gap = len2 - len1; while (gap){ qu1.push_front (0 ); gap--; } } int cf = 0 ; deque<int > ans; deque<int >::reverse_iterator it1, it2; for (it1 = qu1.rbegin (), it2 = qu2.rbegin (); it1 != qu1.rend ();it1++, it2++){ int tmp = *it1 + *it2 + cf; ans.push_front (tmp % 10 ); if (tmp >= 10 )cf = 1 ; else cf = 0 ; } if (cf == 1 ){ ans.push_front (1 ); } for (auto x : ans)cout << x; cout << endl; return 0 ; }
有木有很清晰的感觉~~
高精度运算 高精度
看了几个题,基本都是涉及到大数的加减乘除,因此在这里将其归纳总结起来,再遇到类似的题融会贯通就好。
解决思路 将大数用字符串读取进来以后,使用vector进行逐位运算(在不优化的情况下,使用小学竖式加减乘除的思想就OK),这里可能涉及到结构继承。
代码如下(转自百度百科):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 #include <bits/stdc++.h> using namespace std;struct Wint :vector<int > { Wint (int n=0 ) { push_back (n); check (); } Wint& check () { while (!empty ()&&!back ())pop_back (); if (empty ())return *this ; for (int i=1 ; i<size (); ++i) { (*this )[i]+=(*this )[i-1 ]/10 ; (*this )[i-1 ]%=10 ; } while (back ()>=10 ) { push_back (back ()/10 ); (*this )[size ()-2 ]%=10 ; } return *this ; } }; istream& operator >>(istream &is,Wint &n) { string s; is>>s; n.clear (); for (int i=s.size ()-1 ; i>=0 ; --i)n.push_back (s[i]-'0' ); return is; } ostream& operator <<(ostream &os,const Wint &n) { if (n.empty ())os<<0 ; for (int i=n.size ()-1 ; i>=0 ; --i)os<<n[i]; return os; }bool operator !=(const Wint &a,const Wint &b) { if (a.size ()!=b.size ())return 1 ; for (int i=a.size ()-1 ; i>=0 ; --i) if (a[i]!=b[i])return 1 ; return 0 ; }bool operator ==(const Wint &a,const Wint &b) { return !(a!=b); }bool operator <(const Wint &a,const Wint &b) { if (a.size ()!=b.size ())return a.size ()<b.size (); for (int i=a.size ()-1 ; i>=0 ; --i) if (a[i]!=b[i])return a[i]<b[i]; return 0 ; }bool operator >(const Wint &a,const Wint &b) { return b<a; }bool operator <=(const Wint &a,const Wint &b) { return !(a>b); }bool operator >=(const Wint &a,const Wint &b) { return !(a<b); } Wint& operator +=(Wint &a,const Wint &b) { if (a.size ()<b.size ())a.resize (b.size ()); for (int i=0 ; i!=b.size (); ++i)a[i]+=b[i]; return a.check (); } Wint operator +(Wint a,const Wint &b) { return a+=b; } Wint& operator -=(Wint &a,Wint b) { if (a<b)swap (a,b); for (int i=0 ; i!=b.size (); a[i]-=b[i],++i) if (a[i]<b[i]) { int j=i+1 ; while (!a[j])++j; while (j>i) { --a[j]; a[--j]+=10 ; } } return a.check (); } Wint operator -(Wint a,const Wint &b) { return a-=b; } Wint operator *(const Wint &a,const Wint &b) { Wint n; n.assign (a.size ()+b.size ()-1 ,0 ); for (int i=0 ; i!=a.size (); ++i) for (int j=0 ; j!=b.size (); ++j) n[i+j]+=a[i]*b[j]; return n.check (); } Wint& operator *=(Wint &a,const Wint &b) { return a=a*b; } Wint divmod (Wint &a,const Wint &b) { Wint ans; for (int t=a.size ()-b.size (); a>=b; --t) { Wint d; d.assign (t+1 ,0 ); d.back ()=1 ; Wint c=b*d; while (a>=c) { a-=c; ans+=d; } } return ans; } Wint operator /(Wint a,const Wint &b) { return divmod (a,b); } Wint& operator /=(Wint &a,const Wint &b) { return a=a/b; } Wint& operator %=(Wint &a,const Wint &b) { divmod (a,b); return a; } Wint operator %(Wint a,const Wint &b) { return a%=b; } Wint& operator !(Wint &a){ if (a == Wint (0 ) || a == Wint (1 )){ a = Wint (1 ); return a; } else { Wint n = a; a = Wint (1 ); Wint i (1 ) ; for (;i <= n;i += Wint (1 )){ a *= i; } return a; } }
引爆炸弹 DFS
在一个 n× m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。
现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。
输入格式 第一行输两个整数 n,m,用空格隔开。
接下来 n 行,每行输入一个长度为 m 的字符串,表示地图信息。0
表示没有炸弹,1
表示炸弹。
数据约定:
对于 40% 的数据:1≤n ,m ≤100;
对于 100% 的数据:1≤n ,m ≤500;
输出格式 输出一个整数,表示最少需要手动引爆的炸弹数。
解题思路 使用深度搜索,先引爆一个炸弹,获得一个全新的地图,然后再引爆剩下的炸弹,直到所有的炸弹都被引爆完,然后取较少的引爆次数,直到所有的情况都遍历完获得答案。(这题为什么没有首先考虑广度优先搜索,原因在于地图的面积是很小的,因此在炸弹稀疏的情况下能够较快被引爆完,而选择广度优先搜索的话会占用大量的队列空间)。
代码如下(具体实现步骤看代码注释):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <bits/stdc++.h> using namespace std;struct Point { int x; int y; Point (int x, int y):x (x), y (y) {} }; ostream& operator << (ostream& os, const Point &pos){ cout << "(" << pos.x << "," << pos.y << ")" ; return os; }bool check (const vector<string> &zone,vector<Point> &bombs) { int row, col; row = zone.size (); col = zone[0 ].size (); int i , j; for (i = 0 ;i < row;i++){ for (j = 0 ;j < col;j++){ if (zone[i][j] == '1' ){ bombs.push_back (Point (i, j)); } } } if (bombs.size () == 0 )return false ; else return true ; }void fire_one (vector<string> &zone, Point fire_pos) { int row = fire_pos.x; int col = fire_pos.y; for (int i = 0 ;i < zone.size ();i++){ if (zone[i][col] == '1' ){ zone[i][col] = '0' ; } } for (int j = 0 ;j < zone[0 ].size ();j++){ if (zone[row][j] == '1' ){ zone[row][j] = '0' ; } } }void bfs (vector<string> zone, Point fire_pos, int cur_min, int &min) { fire_one (zone, fire_pos); vector<Point> bombs; if (check (zone, bombs)){ while (bombs.size ()){ bfs (zone, bombs.back (), cur_min + 1 , min); bombs.pop_back (); } } else { if (cur_min < min){ min = cur_min; } } return ; }int fire_bombs (vector<string> &zone) { int min = 0x3f3f3f3f ; vector<Point> bombs; if (check (zone, bombs)){ while (bombs.size ()){ bfs (zone, bombs.back (), 0 , min); bombs.pop_back (); } } return min; }int main () { int n,m; cout << "please input row and col:\n" ; cin >> n >> m; vector<string> zone; string tmp; for (int i = 0 ;i < n;i++){ cin >> tmp; zone.push_back (tmp); } int min = fire_bombs (zone); cout << "min:" << min << endl; return 0 ; }
一维跳棋 BFS
一维跳棋是一种在 1×(2N +1) 的棋盘上玩的游戏。一共有 N 个棋子,其中 N 个是黑的,N个是白的。游戏开始前,N 个白棋子被放在一头,N 个黑棋子被放在另一头,中间的格子空着。在这个游戏里有两种移动方法是允许的:你可以把一个棋子移到与它相邻的空格;你可以把一个棋子跳过一个(仅一个)与它不同色的棋子到达空格。
对于N=3 的情况,棋盘状态依次为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 WWW BBB WW WBBB WWBW BB WWBWB B WWB BWB W BWBWB WBWBWBBW WBWBBWBW WBBWBWBW BWBWB WBWB BWW B BWBWW BB WBWWBBBW WWBBB WWW
对应的空格所在的位置(从左数)为:3 5 6 4 2 1 3 5 7 6 4 2 3 5 4。
输入格式 输入仅一个整数,表示针对 N(1≤N≤8) 的取值。
输出格式 依次输出空格所在棋盘的位置,每个整数间用空格分隔,每行 5个数(每行结尾无空格,最后一行可以不满 5个数;如果有多组移动步数最小的解,输出第一个数最小的解)。
解题思路 求最小的解一般优先考虑广度优先搜索,本题就是这个例子。对于每一种棋况,考虑空格能够移动的情况,而最多的可能情况只有4种:空格往左移动,往右移动一格,当左边(右边)的相邻两个棋子不同时,可以往左(右)跳1格。因此,我们只用检查每一种情况,把符合情况的新棋况放入一格队列中,然后继续取出头部的元素进行搜索,直到遇到结果的情况。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <bits/stdc++.h> using namespace std; map<string, bool > visited;struct stat { string board; int space_index; int parent; stat (string b, int index, int pa):board (b), space_index (index), parent (pa) {} void check (vector<stat> &new_stat, int pa) const { if (space_index != 0 ){ string new_board = board; if (space_index != 1 ){ if (new_board[space_index - 2 ] != new_board[space_index - 1 ]){ new_board[space_index] = new_board[space_index - 2 ]; new_board[space_index - 2 ] = ' ' ; if (visited.find (new_board) == visited.end ()){ new_stat.push_back (stat (new_board, space_index - 2 , pa)); } } } new_board = board; new_board[space_index] = new_board[space_index - 1 ]; new_board[space_index - 1 ] = ' ' ; if (visited.find (new_board) == visited.end ()){ new_stat.push_back (stat (new_board, space_index - 1 , pa)); } } int size = board.size (); if (space_index != size - 1 ){ string new_board = board; new_board[space_index] = new_board[space_index + 1 ]; new_board[space_index + 1 ] = ' ' ; if (visited.find (new_board) == visited.end ()){ new_stat.push_back (stat (new_board, space_index + 1 , pa)); } if (space_index != size - 2 ){ new_board = board; if (new_board[space_index + 1 ] != new_board[space_index + 2 ]){ new_board[space_index] = new_board[space_index + 2 ]; new_board[space_index + 2 ] = ' ' ; if (visited.find (new_board) == visited.end ()){ new_stat.push_back (stat (new_board, space_index + 2 , pa)); } } } } } };struct myQueue :vector<stat>{ int front; myQueue (){front = 0 ;} stat get_front () { return this ->at (front); } void qu_pop () { front++; return ; } };int main () { int n; cout << "please input number of every side:" ; cin >> n; myQueue qu; string start, end; for (int i = 0 ;i < 2 * n + 1 ; i++){ if (i < n){ start.push_back ('w' ); } else if (i == n){ start.push_back (' ' ); } else { start.push_back ('b' ); } } string::reverse_iterator r_it; for (r_it = start.rbegin (); r_it != start.rend (); r_it++){ end.push_back (*r_it); } stat tmp (start, n, -1 ) ; qu.push_back (tmp); visited[tmp.board] = true ; vector<stat> new_stat; while (qu.size () != qu.front){ tmp = qu.get_front (); if (tmp.board == end){ vector<int > ans; while (tmp.parent != -1 ){ ans.push_back (tmp.space_index); tmp = qu.at (tmp.parent); } vector<int >::reverse_iterator re_it; for (re_it = ans.rbegin (); re_it != ans.rend (); re_it++){ cout << *re_it + 1 << " " ; }cout << endl; return 0 ; } int pa = qu.front; qu.qu_pop (); new_stat.clear (); tmp.check (new_stat, pa); vector<stat>::iterator it; for (it = new_stat.begin (); it != new_stat.end (); it++){ visited[it->board] = true ; qu.push_back (*it); } } return 0 ; }
数独 DFS
蒜头君今天突然开始还念童年了,想回忆回忆童年。他记得自己小时候,有一个很火的游戏叫做数独。便开始来了一局紧张而又刺激的高阶数独。蒜头君做完发现没有正解,不知道对不对? 不知道聪明的你能否给出一个标准答案?
标准数独是由一个给与了提示数字的 9×9 网格组成,我们只需将其空格填上数字,使得每一行,每一列以及每一个 3×3 宫都没有重复的数字出现。
输入格式 一个 9×9 的数独,数字之间用空格隔开。0
表示需要填写的数字。
输出格式 输出一个 9×9 的数独,把出入中的0
替换成需要填写的数字即可。
解题思路 由于只用求出一个解,所以使用深度优先搜索,由于在填数字的过程中需要频繁地查找地图中的元素以判断是否违反规则。如果直接查找,则每一次都需要消耗大约9+9+9个时间单位,但其实这是没有必要的,因为我们只需要知道和它相同的值是否在同一条线或者同一个九宫格内就行,因此这里可以使用哈希表来降低查找时间。
对于查找到的每一个空格,我们需要遍历数字1~9,如果填入该数字后不会违反规则,我们就填入它,然后查找下一个空格,直到这个查找的指针遍历到最后一个位置,算法结束。如果当前空格已经遍历完所有可能的数字,则将该位置重置为0,并且从哈希表中删除它,再回到前一个位置进行遍历。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 #include <bits/stdc++.h> using namespace std;class Point {public : int row; int col; Point (int r, int c){ row = r; col = c; } bool equal (Point a) { return a.row == row && a.col == col; } }; unordered_map<int , vector<Point> > points; vector<vector<int > > board (9 , vector <int >(9 , 0 ));void init_board () { string input; cout << "format: " << "123456789\n" ; cout << "-----------------\n" ; for (int i = 0 ; i < 9 ;i++){ cout << "line " << i + 1 << ": " ; getline (cin, input); for (int j = 0 ; j < 9 ; j++){ board[i][j] = (int )(input[j] - '0' ); if (board[i][j])points[board[i][j]].push_back (Point (i, j)); } } }bool check (int val, Point pos) { for (auto it = points[val].begin (); it != points[val].end (); it++){ if (pos.row == it->row || pos.col == it->col)return false ; if (pos.row/3 == it->row/3 && pos.col/3 == it->col/3 )return false ; } return true ; }void del (int val, Point pos) { for (auto it = points[val].begin (); it != points[val].end (); it++){ if (it->equal (pos)){ points[val].erase (it); return ; } } }Point init_pos (Point pos) { while (board[pos.row][pos.col]){ if (pos.equal (Point (8 , 8 ))){ cout << "it's full!\n" ; exit (1 ); } if (pos.col == 8 ){ pos.row++; pos.col = 0 ; } else { pos.col++; } } return pos; }void disp_board () { for (int i = 0 ;i < 9 ;i++){ for (int j = 0 ; j < 9 ;j++){ printf (",%d" + !j, board[i][j]); }cout << endl; } }bool DFS (Point pos) { for (int i = 1 ; i <= 9 ;i++){ if (check (i, pos)){ board[pos.row][pos.col] = i; points[i].push_back (pos); Point new_pos (pos) ; while (board[new_pos.row][new_pos.col]){ if (new_pos.equal (Point (8 , 8 ))){ disp_board (); return true ; } if (new_pos.col == 8 ){ new_pos.row++; new_pos.col = 0 ; } else { new_pos.col++; } } if (DFS (new_pos))return true ; board[pos.row][pos.col] = 0 ; del (i, pos); } } return false ; }int main () { init_board (); if (!DFS (init_pos (Point (0 ,0 ))))cout << "no solution!\n" ; return 0 ; }
任务系统 优先队列
蒜头君设计了一个任务系统。这个系统是为了定时提醒蒜头君去完成一些事情。
系统大致如下,初始的时候,蒜头君可能会注册很多任务,每一个任务的注册如下:
Register $Q_{num}$ Period
表示从系统启动开始,每过 Period 秒提醒蒜头君完成编号为 $Q_{num}$的任务。
你能计算出蒜头君最先被提醒的 k个任务吗?
输入格式 第一行输入 n(0<n≤50000),k(0<k≤10000),其中 n表示蒜头君注册的任务数量。
接下来 n行,每行输入一条注册命令,其中0<$q_{num}$≤3000,0≤Period≤3000。
输出格式 顺序输出 k行,表示依次提醒的任务的编号。如果同一时间有多个任务,最先提醒编号小的任务。
解题思路 使用优先队列(堆结构),每次弹出时间最近的任务后,更新该任务的下一次触发时间放入队列中排序,直到前k次输出完。
eg: 堆结构经常是在插入和弹出之间交替进行的。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <bits/stdc++.h> using namespace std; map<int , int > single_time;struct task { int num, time; task (int x, int y):num (x), time (y) {} bool operator < (const task &a) const { if (time > a.time)return true ; if (time == a.time && num > a.num)return true ; return false ; } }; priority_queue<task> qu;int main () { int n ,k; cin >> n >> k; string buff; int num, time; while (n--){ cin >> buff >> num >> time; single_time[num] = time; qu.push (task (num, time)); } while (k--){ task tmp = qu.top (); qu.pop (); cout << tmp.num << endl; tmp.time += single_time[tmp.num]; qu.push (tmp); } return 0 ; }