两套蓝桥杯热身题"/>
两套蓝桥杯热身题
两套蓝桥杯热身题
分类: HHUC2014-03-08 21:18 79人阅读 评论(0) 收藏 举报
[140222]
0.总结:
比较顺利,比较快得做出了第一题和第二题,第三题的时候第一想法是用STL中的全排列函数(next_permutation()),后续发现改起来有点麻烦,于是就直接写回溯的全排列了。第四题对DP理解不深,用暴搜的方法能过几个点算几个点。
1.出栈顺序统计
题意:栈是常用的一种数据结构,有N个元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列。你已经知道栈的操作有两种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。现在要使用这两种操作,由一系列操作序列可以得到一系列的输出序列。请你编程求出对于给定的N,计算并输出由等待进栈的序列1,2,……,N,经过一系列操作可能得到的输出序列总数。(1<=n<=15)
思路:数据量不大,穷举所有的可能性。不妨假设栈中已有N个元素,现将X放入栈。其放入的顺序可以在原有栈中不弹出元素已经弹出所有元素之间
代码:
[cpp] view plaincopy
- #include <iostream>
- #include <stack>
- #include <cstdio>
- #include <string>
- using namespace std;
- void dfs(int stack_top, int num);
- int n;
- int ans = 0;
- int main()
10. {
- 11. freopen("stack.in", "r", stdin);
- 12. freopen("stack.out", "w", stdout);
- 13. cin >> n;
- 14. dfs(0, 1);
- 15. cout << ans << endl;
- 16. return 0;
17. }
- 18.
19. void dfs(int stack_top, int num){
- 20. if(num == n+1){
- 21. ans++;
- 22. return;
- 23. }
- 24.
- 25. for(int i = 0; i <= stack_top; ++i){
- 26. dfs(stack_top-i+1, num+1);
- 27. }
28. }
2.走迷宫
题意:有一个M*N格的迷宫(表示有M行、N列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这M*N个数据和起始点、结束点的位置。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出-1。
思路:DFS,注意回溯。学习到一个小技巧,走迷宫的时候不需要另外再设一个vis数组,直接在map数组上进行修改就好,这样可以减少操作数。另外需要注意的是把起始点设置为已访问
代码:
[cpp] view plaincopy
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- #define MAXN 15
- using namespace std;
- bool map[MAXN][MAXN];
- const int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
- int ans = 0;
- int m, n;
10. int start_x, start_y, fin_x, fin_y;
11. void dfs(int x, int y);
12. bool check(int x, int y);
13. int main()
14. {
- 15. freopen("maze.in" ,"r", stdin);
- 16. freopen("maze.out", "w", stdout);
- 17. memset(map, false, sizeof(map));
- 18. scanf("%d %d", &m, &n);
- 19. int temp;
- 20. for(int i = 0; i < m; ++i){
- 21. for(int j = 0; j < n; ++j){
- 22. scanf("%d", &temp);
- 23. if(temp){
- 24. map[i][j] = true;
- 25. }
- 26. }
- 27. }
- 28. scanf("%d%d", &start_x, &start_y);
- 29. scanf("%d%d", &fin_x, &fin_y);
- 30. start_x--, start_y--, fin_x--, fin_y--;
- 31. map[start_x][start_y] = false;
- 32. dfs(start_x, start_y);
- 33. if(ans == 0){
- 34. cout << -1 << endl;
- 35. }
- 36. else{
- 37. cout << ans << endl;
- 38. }
- 39. return 0;
40. }
- 41.
42. void dfs(int x, int y){
- 43. if(x == fin_x && y == fin_y){
- 44. ans++;
- 45. return;
- 46. }
- 47. for(int i = 0; i < 4; ++i){
- 48. if(check(x+dir[i][0], y+dir[i][1])){
- 49. map[x+dir[i][0]][y+dir[i][1]] = false;
- 50. dfs(x+dir[i][0], y+dir[i][1]);
- 51. map[x+dir[i][0]][y+dir[i][1]] = true;
- 52. }
- 53. }
54. }
- 55.
56. bool check(int x, int y){
- 57. if(map[x][y] == false || x < 0 || x >= m || y < 0 || y >= n){
- 58. return false;
- 59. }
- 60. return true;
61. }
3.组合的输出
题意:排列与组合是常用的数学方法,其中组合就是从N个元素中抽出R个元素(不分顺序且R<=N),我们可以简单地将N个元素理解为1,2,…,N,从中任取R个数。
例如N=5,R=3,所有组合为:123 124 125 134 135 145 234 235 245 345
思路:回溯先放第一个数,再在第一个数字的基础上放第二个数字,再在第二个数字的基础上放第三个数字
代码:
[cpp] view plaincopy
- #include <iostream>
- #include <cstdio>
- #include <algorithm>
- #include <string>
- using namespace std;
- int ans[25];
- void dfs(int postion, int value);
- int n, r;
- void output();
10. int main()
11. {
- 12. freopen("compages.in", "r", stdin);
- 13. freopen("compages.out", "w", stdout);
- 14. scanf("%d%d", &n, &r);
- 15. dfs(0, 1);
- 16. return 0;
17. }
- 18.
19. void dfs(int postion, int value){
- 20. if(postion == r){
- 21. output();
- 22. return;
- 23. }
- 24. if(value > n){
- 25. return;
- 26. }
- 27. for(int i = value; i <= n; ++i){
- 28. ans[postion] = i;
- 29. dfs(postion+1, i+1);
- 30. }
31. }
- 32.
33. void output(){
- 34. cout << ans[0];
- 35. for(int i = 1; i < r; ++i){
- 36. cout << " " << ans[i];
- 37. }
- 38. cout << endl;
39. }
4.关路灯
题意:某个村庄在一条路线上安装了N盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一个路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。
为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能最省电。他每天都是在天亮时首先关掉自己所在处位置的路灯,然后可以向左向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地掉头有可能会更省一些。
现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短可以忽略不计。
请你为老张编一个程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。
思路:当初测试的时候因为DP没有思路就直接写了暴力搜索,结合网上的题解
设f[i,j,0]表示第i到j个路灯关完停在左边的最小耗费,f[i,j,1]为右边
g[i,j]表示除了i到j路灯,其他路灯每秒耗费总和
f[i,j,0]=min{f[i+1,j,1]+g[i+1,j]*(dis[j]-dis[i]),f[i+1,j,0]+g[i+1,j]*(dis[i+1]-dis[i])}
f[i,j,1]=min{f[i,j-1,1]+g[i,j-1]*(dis[j]-dis[j-1]),f[i,j-1,0]+g[i,j-1]*(dis[j]-dis[i])}
边界:f[i,j,1]=f[i,j,0]=maxlongint f[k,k,1]=f[k,k,0]=0
ans=min{f[1,n,0],f[1,n,1]}
[cpp] view plaincopy
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define INFINITE 0xFFFFFFF
- #define MAXN 1020
- using namespace std;
- long long f[MAXN][MAXN][2];
- long long g[MAXN][MAXN];
10. int dis[MAXN];
11. int light[MAXN];
12. int sum = 0;
13. int SUMUP(int low, int high);
14. int main(){
- 15. int n, v;
- 16. memset(f, 0, sizeof(f));
- 17. memset(g, 0, sizeof(g));
- 18. memset(light, 0, sizeof(light));
- 19. cin >> n >> v;
- 20. for(int i = 1; i <= n; ++i){
- 21. cin >> dis[i] >> light[i];
- 22. sum += light[i];
- 23. }
- 24. for(int i = 1; i <= n; ++i){
- 25. for(int j = 1; j <= n; ++j){
- 26. g[i][j] = sum-SUMUP(i, j);
- 27. }
- 28. }
- 29. for(int i = 1; i <= n; ++i){
- 30. for(int j = 1; j <= n; ++j){
- 31. f[i][j][0] = INFINITE;
- 32. f[i][j][1] = INFINITE;
- 33. }
- 34. }
- 35. f[v][v][1] = 0;
- 36. f[v][v][0] = 0;
- 37. for(int j = v; j <= n; ++j){
- 38. for(int i = j-1; i >= 1; --i){
- 39. f[i][j][0] = min(f[i+1][j][1]+g[i+1][j]*(dis[j]-dis[i]), f[i+1][j][0]+g[i+1][j]*(dis[i+1]-dis[i]));
- 40. f[i][j][1] = min(f[i][j-1][1]+g[i][j-1]*(dis[j]-dis[j-1]), f[i][j-1][0]+g[i][j-1]*(dis[j]-dis[i]));
- 41. }
- 42. }
- 43. cout << min(f[1][n][0], f[1][n][1]) << endl;
- 44. return 0;
45. }
- 46.
47. long long SUMUP(int low, int high){
- 48. long long ans = 0;
- 49. for(int i = low; i <= high; ++i){
- 50. ans += light[i];
- 51. }
- 52. return ans;
53. }
[140308]
0.总结
看到第一题的时候以为是BFS,必然要状态压缩,还不怎么会,就先跳到第二题了。第二题在USACO上做过,一个简单的找环问题。做完第二题去看第一题的时候发现第一题其实是贪心,然后也就很快得做出来了。第三题的话,是一道区间DP。首先就样例在草稿纸上演算,就可以找出DP的规律了。感觉区间DP的话应该都先找一个简单的输入来模拟一下过程,然后抽象出关系。可惜最后卡在路径输出了,华丽丽的0分。
1.翻硬币
题意:小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用*表示正面,用 o表示反面(是小写字母,不是零)。
比如,可能情形是:**oo***oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作;
思路:第一眼BFS+状态压缩(都是因为那个“最少”),第二眼发现了贪心的本质。
首先我们来看一下无解的情况:一次翻转有三种情况,** -> oo || oo-> ** || o* -> *o 从中我们可以看出,每次的翻转都可以且只会可以造成状态中偶数个正面的变化。所以如果目标状态与初始状态中偶数(或奇数)的数量差为奇数时,则必然无解。
接下来我们来看一下有解的情况是如何贪心的:对初始状态和目标状态的硬币,从左向右依次比较,每当出现硬币正反面不一致的时候,在该位置的基础下寻找下一个不同的位置,然后将这两个“交换”。这里能说“交换”是因为翻转操作类似与换到下一个位置。反证法可以证明此贪心的正确名。
[cpp] view plaincopy
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #define SWAP(a,b) (a)^=(b), (b)^=(a), (a)^=(b)
- #define MAXN 1500
- using namespace std;
- char str1[MAXN];
- char str2[MAXN];
10. int main()
11. {
- 12. freopen("coin.in", "r", stdin);
- 13. freopen("coin.out", "w", stdout);
- 14. int cnt1 = 0, cnt2 = 0;
- 15. int ans = 0;
- 16. scanf("%s%s", str1, str2);
- 17. if(strlen(str1) != strlen(str2)){
- 18. printf("-1\n");
- 19. return 0;
- 20. }
- 21. for(int i = 0; i < strlen(str1); ++i){
- 22. if(str1[i] == '*'){
- 23. cnt1++;
- 24. }
- 25. }
- 26. for(int i = 0; i < strlen(str2); ++i){
- 27. if(str2[i] == '*'){
- 28. cnt2++;
- 29. }
- 30. }
- 31. if(((int)fabs(cnt1-cnt2)) % 2 != 0){
- 32. printf("-1\n");
- 33. return 0;
- 34. }
- 35. for(int i = 0; i < strlen(str1); ++i){
- 36. if(str1[i] != str2[i]){
- 37. for(int j = i+1; j < strlen(str1); ++j){
- 38. if(str1[j] != str2[j]){
- 39. ans += (j-i);
- 40. str1[i] = str2[i];
- 41. str1[j] = str2[j];
- 42. }
- 43. }
- 44. }
- 45. }
- 46. cout << ans << endl;
- 47. return 0;
48. }
2.分数转换
题意:任何两个有理数整数相除都可以表示为无限循环小数的形式。例如:1/7= 0.142857142... 是个无限循环小数。本题的要求是给出一个分子和分母都是有理整数的数,求这个分数的小数表示。
思路:记得长除法吗?我们知道只有当出现了曾经出现过的余数时,小数部分才会出现重复。重复的部分就是自从我们上次见到同样的余数之后计算出的部分。 我们先读入并打印整数部分。接下来,我们对剩下的真分数部分进行长除直到我们发现了重复的余数或余数变为0。如果我们发现了重复的余数,即出现了循环节,就分别恰当地打印重复的部分和不重复的部分。如果余数变为0,即已经除尽,就打印整个小数部分。如果小数位根本没有被生成,那么打印一个0就是正确答案.<USACO,哪天我也能这样清楚得表达自己得多幸福,>
代码:
USACO
[cpp] view plaincopy
- #include <iostream.h>
- #include <fstream.h>
- #include <math.h>
- ofstream out("fracdec.out");
- int colcount=0;
- int numBeforeRepeat(int n, int d) {
- int c2=0, c5=0;
- 10. if (n == 0) return 1;
- 11. while (d%2==0) { d/=2; c2++; }
- 12. while (d%5==0) { d/=5; c5++; }
- 13. while (n%2==0) { n/=2; c2--; } /* 可以抵消 */
- 14. while (n%5==0) { n/=5; c5--; } /* 可以抵消 */
- 15. if (c2>c5)
- 16. if (c2>0) return c2;
- 17. else return 0;
- 18. else
- 19. if (c5>0) return c5;
- 20. else return 0;
21. }
- 22.
23. void print (char c) {
- 24. if (colcount==76) {
- 25. out<<endl;
- 26. colcount=0;
- 27. }
- 28. out<<c;
- 29. colcount++;
30. }
- 31.
32. void print (int n) {
- 33. if (n>=10) print (n/10);
- 34. print ((char)('0'+(n%10)));
35. }
- 36.
37. int main() {
- 38. int n, d;
- 39. ifstream in("fracdec.in");
- 40. in>>n>>d;
- 41. in.close();
- 42.
- 43. print (n/d);
- 44. print ('.');
- 45. n=n%d;
- 46. int m=numBeforeRepeat(n,d);
- 47. for(int i=0;i<m;i++) {
- 48. n*=10;
- 49. print (n/d);
- 50. n%=d;
- 51. }
- 52. int r=n;
- 53. if(r!=0) {
- 54. print ('(');
- 55. do {
- 56. n*=10;
- 57. print (n/d);
- 58. n%=d;
- 59. } while (n!=r);
- 60. print (')');
- 61. }
- 62. out<<endl;
- 63. out.close();
- 64. exit (0);
65. }
我写的
[cpp] view plaincopy
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- using namespace std;
- typedef struct{
- int son;
- int val;
- }Node;
- Node node[10000];
10. int main()
11. {
- 12. freopen("fraction.in", "r", stdin);
- 13. freopen("fraction.out", "w", stdout);
- 14. for(int i = 0; i < 10000; ++i){
- 15. node[i].son = 0;
- 16. node[i].val = 0;
- 17. }
- 18. int a, b;
- 19. int c;
- 20. int temp;
- 21. scanf("%d/%d", &a, &b);
- 22. for(int i = 2; i < a && i < b; ++i){
- 23. if(a % i == 0 && b % i == 0){
- 24. a /= i;
- 25. b /= i;
- 26. }
- 27. }
- 28. if(a%b == 0){
- 29. printf("%d\n", a/b);
- 30. return 0;
- 31. }
- 32. printf("%d.",a/b);
- 33. a = a % b;
- 34. c = a;
- 35. temp = b;
- 36. while(temp % 2 == 0){
- 37. temp /= 2;
- 38. }
- 39. while(temp % 5 == 0){
- 40. temp /= 5;
- 41. }
- 42. if(temp == 1){
- 43. while(a != 0){
- 44. a *= 10;
- 45. printf("%d", a/b);
- 46. a -= a/b*b;
- 47. }
- 48. return 0;
- 49. }
- 50. else{
- 51. while(node[a].son == 0){
- 52. a *= 10;
- 53. node[a/10].val = a/b;
- 54. node[a/10].son = a-a/b*b;
- 55. a -= a/b*b;
- 56. }
- 57. }
- 58. while(c != a){
- 59. printf("%d", node[c].val);
- 60. c = node[c].son;
- 61. }
- 62. printf("[");
- 63. printf("%d", node[c].val);
- 64. c = node[c].son;
- 65. while(c!=a){
- 66. printf("%d", node[c].val);
- 67. c = node[c].son;
- 68. }
- 69. printf("]\n");
- 70. return 0;
71. }
3.书的复制
题意:现在要把M本有顺序的书分给K个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
思路:以dp[i][j]表示前i个人完成前j本书所需要的最少时间,那么我们可以知道dp[i+1][k] = min(max(dp[i][j], SUM(j+1, k))); 此处k取遍可能性。DP题一定要在草稿纸上挑一个简单的输入来手工模拟一下。输出时需要贪心,在测试时没想清楚。。
代码:
[cpp] view plaincopy
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #define INFINITE 0xFFFFFFF
- #define MAX(a,b) (a)>(b)?(a):(b)
- #define MIN(a,b) (a)>(b)?(b):(a)
- int SumUp(int lower, int higher);
- using namespace std;
- int dp[500+5][500+5];
10. int book[500+5];
11. int pos[500+5];
12. int cnt = 0;
13. int num, peo;
14. int main()
15. {
- 16. int ans;
- 17. int temp;
- 18. memset(dp, 0, sizeof(dp));
- 19. memset(book, 0, sizeof(book));
- 20. scanf("%d%d", &num, &peo);
- 21. for(int i = 1; i <= num; ++i){
- 22. scanf("%d", &book[i]);
- 23. dp[1][i] = SumUp(1, i);
- 24. }
- 25. for(int i = 2; i <= peo; ++i){
- 26. for(int j = 1; j <= num; ++j){
- 27. ans = 0xFFFFFFF;
- 28. for(int k = 1; k < j; ++k){
- 29. ans = MIN(ans, MAX(dp[i-1][k], SumUp(k+1, j)));
- 30. }
- 31. dp[i][j] = ans;
- 32. }
- 33. }
34. // for(int i = 1; i <= peo; ++i){
35. // for(int j = 1; j <= num; ++j){
36. // printf("%d ", dp[i][j]);
37. // }
38. // printf("\n");
39. // }
- 40. int t = dp[peo][num];
- 41. int sum = 0;
- 42. int tmp = peo;
- 43. pos[tmp] = num;
- 44. for(int i = num; i; --i){
- 45. sum += book[i];
- 46. if(sum > t){
- 47. pos[--tmp] = i;
- 48. sum = book[i];
- 49. }
- 50. }
- 51. for(int i = 1; i <= peo; ++i){
- 52. printf("%d %d\n", pos[i-1]+1, pos[i]);
- 53. }
- 54. return 0;
55. }
- 56.
57. int SumUp(int lower, int higher){
- 58. int sum = 0;
- 59. for(int i = lower; i <= higher; ++i){
- 60. sum += book[i];
- 61. }
- 62. return sum;
63. }
转载于:.html
更多推荐
两套蓝桥杯热身题
发布评论