2018年全国多校算法寒假训练营练习比赛(第一场)题解
2018-01-21 20:09
831 查看
A:贪心+暴力
链接:大吉大利,今晚吃鸡——枪械篇
思路:我用了一个map使配件种类对应一个最大威力的配件。然后暴力判断每把枪都装上最优的配件以后的威力,求一个极大值即可。
B:vector模拟
链接:最强的决斗者一切都是必然的!
思路:直接模拟就好了,题意比较难读,主要需要看样例。
C:模拟
链接:六子冲
思路:共有八种情况,模拟一下即可。情况比较多,别错了就好。实在是没想到其他好的办法,感觉自己的方法太蠢了。
D:规律
链接:N阶汉诺塔变形
思路:我不会,哈哈哈。参考大神题解勉强理解。
大神题解链接:N阶汉诺塔变形
E:深搜
链接:恋与程序员
思路:其实题意就是在一个N个点M条边的图中,求1~C的最短路,只不过边权和平时的不太一样,边权为卡片的种类,卡片只需要买一次,共有K种卡片,每张卡片有对应的花费。问1~C的最小花费。
深搜,走过的点进行标记,用过的卡片进行标记,当走某一条边的时候分两种情况,如果这个边的那张卡片买过了就不花钱,否则就需要花钱。用一个全局变量统计答案,每次走到终点C看是否需要更新答案。
当时直接想到了最短路,发现不会修改,然后就GG了。赛后看了眼别人的代码,原来是个深搜,很快就A了。
F:模拟
链接:大吉大利,今晚吃鸡——跑毒篇
思路:朋友说有坑,我没感觉到,迷之AC。每次使离终点步数减一,步数减一之后就掉血,如果<=0那就GG,如果没有判断是否需要打血,即血量小于7*a且大于6*a,如果小于等于6*a的话打血过程中会被毒死,如果大于等于7*a的话还可以再跑一米再打血。
一个队友把使用急救包的意思理解为直接+80血了(其实是把血两补到80),结果wa到怀疑人生。
后来他说:吃了没玩过吃鸡的亏哈哈哈。
G:递归 + 找规律
链接:圆圈
思路:估计是POJ ~ 2083的改编。图形可以分为上下左右四个部分,递归打印即可。主要是找到规律:对于每阶图形,由该阶图形左上角的点,可以推出其他下一阶图形的左上角的点的位置,如果只有一个点了那么就直接赋值为'O'。
H:斐波那契数列
链接:方块与收纳盒
思路:听见他们说是斐波那契数列,然后就敲一发试试,发现数据不超long long,直接A了
I:暴力
链接:找数字个数
思路:求出先求出a和b数字的每一位数字,放入set。然后暴力判断1~1000,是a和b倍数的直接跳过,分理处当前数字的每一位,如果某一位在set中出现过,也跳过,否则答案++。
J:规律 + 递归
链接:闯关的lulu
思路:主要是题意,每走一层他会获得一个0,
2个0 == 1个1
3个1 == 1个2
4个2 == 1个3
5个3 == 1个4
。。。
可以推出来:n个k == n/(k-2)个k
他走N层总共会获得N个0,然后我们递归去算有几个几就好了,然后回溯过程种输出。
注意N的奇偶性,如果N为奇数直接在最后输一个0。
链接:大吉大利,今晚吃鸡——枪械篇
思路:我用了一个map使配件种类对应一个最大威力的配件。然后暴力判断每把枪都装上最优的配件以后的威力,求一个极大值即可。
#include<bits/stdc++.h> using namespace std; const int MAXN = 1005; int n, m, k; double p[MAXN];//枪的威力 set<int> s[MAXN];//配件种类 map<int, double> M;//最优配件 int main() { while (~scanf("%d%d", &n, &m)) { M.clear(); for (int i = 0; i < n; i++) { s[i].clear(); scanf("%lf%d", &p[i], &k); while (k--) { int t; scanf("%d", &t); s[i].insert(t); } } while (m--) { int q; double b; scanf ("%d%lf", &q, &b); if (M.count(q)) { M[q] = max(M[q], b);//保存q配件的最大威力 } else M[q] = b; } double MAX = -1.0; for (int i = 0; i < n; i++) { double t = 1.0; for (auto j: s[i]) { t = t + M[j]; } MAX = max(MAX, t * p[i]); } printf("%.4f\n", MAX); } return 0; } /* 3 6 120 3 1 2 3 100 4 1 2 3 4 110 3 2 3 4 1 0.12 2 0.23 2 0.26 4 0.57 3 0.35 5 0.41 */
B:vector模拟
链接:最强的决斗者一切都是必然的!
思路:直接模拟就好了,题意比较难读,主要需要看样例。
#include<bits/stdc++.h> using namespace std; const int MAXN = 1005; int n; struct Node { int s, t, x; }a[MAXN]; int main() { while (~scanf("%d", &n)) { for (int i = 1; i <= n; i++) { scanf("%d%d", &a[i].s, &a[i].t); if (a[i].t == 1 || a[i].t == 2) scanf("%d", &a[i].x); } long long ans = 0;//伤害 vector<Node> v;//连锁序列,下标表示连锁数 for (int i = 1; i <= n; i++) { if (a[i].s >= a[i - 1].s)//连锁 { v.push_back(a[i]);//放入vector } else//连锁结束 { //计算此次连锁的卡牌的伤害 for (int j = v.size() - 1; j >= 0; j--) { if (v[j].t == 3) break;//所有卡牌无效,则直接跳出 if (v[j].t == 4) { j--; continue; }//上一张卡牌无效 if (v[j].t == 1) ans = ans + v[j].x;//伤害为x if (v[j].t == 2) ans = ans + (j + 1) * v[j].x;//伤害为x*连锁数 } v.clear();//清空连锁卡牌 v.push_back(a[i]);//新的连锁 } } //计算最后一次连锁伤害 for (int j = v.size() - 1; j >= 0; j--) { if (v[j].t == 3) break; if (v[j].t == 4) { j--; continue; } if (v[j].t == 1) ans = ans + v[j].x; if (v[j].t == 2) ans = ans + (j + 1) * v[j].x; } printf("%lld\n", ans); } return 0; } /* 9 1 1 300 2 2 400 2 3 2 2 500 1 1 1000 3 4 2 1 600 3 3 3 4 */
C:模拟
链接:六子冲
思路:共有八种情况,模拟一下即可。情况比较多,别错了就好。实在是没想到其他好的办法,感觉自己的方法太蠢了。
#include<bits/stdc++.h> using namespace std; int n, a[10][10], b[10], dir[5][2] = {{0, 0}, {-1, 0}, {1, 0}, {0, -1}, {0, 1}}; void init() { memset(a, 0, sizeof(a)); //黑棋 a[3][1] = 1; a[4][1] = 2; a[4][2] = 3; a[4][3] = 4; a[4][4] = 5; a[3][4] = 6; //白棋 a[2][4] = 7; a[1][4] = 8; a[1][3] = 9; a[1][2] = 10; a[1][1] = 11; a[2][1] = 12; } void Find(int q, int& row, int& col)//求得q棋子的位置 { for (int i = 1; i <= 4; i++) { for (int j = 1; j <= 4; j++) { if (a[i][j] == q) { row = i; col = j; } } } } int judge(int num) { if (1 <= num && num <= 6) return 1;//黑 else if(7 <= num && num <= 12) return -1;//白 return 0;//空 } void work(int row, int col) { for(int i = 1; i <= 4; i++) b[i] = judge(a[row][i]);//消除行 if((b[2]==1 && b[3]==1 && b[1]==-1 && judge(a[row][col])==1 || b[2]==-1 && b[3]==-1 && b[1]==1 && judge(a[row][col])==-1) && b[4]==0) a[row][1] = 0; if((b[3]==1 && b[4]==1 && b[2]==-1 && judge(a[row][col])==1 || b[3]==-1 && b[4]==-1 && b[2]==1 && judge(a[row][col])==-1) && b[1]==0) a[row][2] = 0; if((b[1]==1 && b[2]==1 && b[3]==-1 && judge(a[row][col])==1 || b[1]==-1 && b[2]==-1 && b[3]==1 && judge(a[row][col])==-1) && b[4]==0) a[row][3] = 0; if((b[2]==1 && b[3]==1 && b[4]==-1 && judge(a[row][col])==1 || b[2]==-1 && b[3]==-1 && b[4]==1 && judge(a[row][col])==-1) &a 117ab mp;& b[1]==0) a[row][4] = 0; for(int i = 1; i <= 4; i++) b[i] = judge(a[i][col]);//消除列 if((b[2]==1 && b[3]==1 && b[1]==-1 && judge(a[row][col])==1 || b[2]==-1 && b[3]==-1 && b[1]==1 && judge(a[row][col])==-1) && b[4]==0) a[1][col] = 0; if((b[3]==1 && b[4]==1 && b[2]==-1 && judge(a[row][col])==1 || b[3]==-1 && b[4]==-1 && b[2]==1 && judge(a[row][col])==-1) && b[1]==0) a[2][col] = 0; if((b[1]==1 && b[2]==1 && b[3]==-1 && judge(a[row][col])==1 || b[1]==-1 && b[2]==-1 && b[3]==1 && judge(a[row][col])==-1) && b[4]==0) a[3][col] = 0; if((b[2]==1 && b[3]==1 && b[4]==-1 && judge(a[row][col])==1 || b[2]==-1 && b[3]==-1 && b[4]==1 && judge(a[row][col])==-1) && b[1]==0) a[4][col] = 0; } int main() { int CASE = 1; while (~scanf("%d", &n)) { init(); for (int i = 0; i < n; i++) { int q, p; scanf("%d%d", &q, &p); int row, col; Find(q, row, col); int row2 = row + dir[p][0], col2 = col + dir[p][1]; swap(a[row][col], a[row2][col2]);//走子 work(row2, col2);//吃子 } printf("#Case %d:\n", CASE++); for (int i = 1; i <= 4; i++) { for (int j = 1; j <= 4; j++) { printf("%3d", a[i][j]); } printf("\n"); } } return 0; } /* 8 7 3 6 1 12 4 1 1 12 2 2 1 10 2 4 1 */
D:规律
链接:N阶汉诺塔变形
思路:我不会,哈哈哈。参考大神题解勉强理解。
大神题解链接:N阶汉诺塔变形
#include<bits/stdc++.h> using namespace std; long long n, k; int main() { while (~scanf("%lld%lld", &n, &k)) { vector<int> v[3]; long long base = 1; for (int i = 1; i <= n; i++) { int t = (k / base) % 6; if (t > 2) t = 5 - t; v[t].push_back(i); base *= 3; } for (int i = 0; i < 3; i++) { if (v[i].size()) { for (int j = v[i].size() - 1; j >= 0; j--) { printf("%d", v[i][j]); if (j != 0) printf(" "); } } else printf("0"); printf("\n"); } } return 0; } /* 3 5 4 10 */
E:深搜
链接:恋与程序员
思路:其实题意就是在一个N个点M条边的图中,求1~C的最短路,只不过边权和平时的不太一样,边权为卡片的种类,卡片只需要买一次,共有K种卡片,每张卡片有对应的花费。问1~C的最小花费。
深搜,走过的点进行标记,用过的卡片进行标记,当走某一条边的时候分两种情况,如果这个边的那张卡片买过了就不花钱,否则就需要花钱。用一个全局变量统计答案,每次走到终点C看是否需要更新答案。
当时直接想到了最短路,发现不会修改,然后就GG了。赛后看了眼别人的代码,原来是个深搜,很快就A了。
#include<bits/stdc++.h> using namespace std; const int INF = 1e9 + 5; const int MAXN = 105; int n, m, k, c, ans; int G[MAXN][MAXN], card[1005];//G为图,card[i]表示编号为i的卡片价格为card[i] bool vis[MAXN];//该点有没有被走过 bool use[1005];//卡片是否被使用 void dfs(int u, int sum) { if (u == c){ ans = min(ans, sum); return ; } for (int v = 1; v <= n; v++) { if (G[u][v] && !vis[v])//有一条u到v的边,且v点没被走过 { vis[v] = true; if (use[G[u][v]]) dfs(v, sum);//该卡片被用过 else//该卡片没被用过 { use[G[u][v]] = true; dfs(v, sum + card[G[u][v]]); use[G[u][v]] = false; } vis[v] = false; } } } int main() { while (~scanf("%d%d%d%d", &n, &m, &k, &c)) { ans = INF; memset(G, 0, sizeof(G)); memset(vis, 0, sizeof(vis)); memset(use, 0, sizeof(use)); for (int i = 0; i < m; i++) { int u, v, e; scanf("%d%d%d", &u, &v, &e); G[u][v] = e; } for (int i = 0; i < k; i++) { int a, b; scanf("%d%d", &a, &b); card[a] = b; } vis[1] = true; dfs(1, 0); printf("%d\n", ans); } return 0; } /* 6 7 5 6 2 3 2 4 3 3 1 2 1 1 5 4 4 6 5 1 4 2 5 6 3 1 100 3 422 2 210 5 107 4 38 */
F:模拟
链接:大吉大利,今晚吃鸡——跑毒篇
思路:朋友说有坑,我没感觉到,迷之AC。每次使离终点步数减一,步数减一之后就掉血,如果<=0那就GG,如果没有判断是否需要打血,即血量小于7*a且大于6*a,如果小于等于6*a的话打血过程中会被毒死,如果大于等于7*a的话还可以再跑一米再打血。
一个队友把使用急救包的意思理解为直接+80血了(其实是把血两补到80),结果wa到怀疑人生。
后来他说:吃了没玩过吃鸡的亏哈哈哈。
#include<bits/stdc++.h> using namespace std; int main() { int T; scanf("%d", &T); while (T--) { int a, b, c, h = 100; scanf("%d%d%d", &a, &b, &c); bool flag = true; b--; while (b--) { h = h - a; if (h <= 0) { flag = false; break; } if (6 * a < h && h <= 7 * a && c >= 1) { h = 80; c--; } } if (flag) printf("YES\n"); else printf("NO\n"); } return 0; } /* 3 1 100 2 6 31 2 7 31 2 */
G:递归 + 找规律
链接:圆圈
思路:估计是POJ ~ 2083的改编。图形可以分为上下左右四个部分,递归打印即可。主要是找到规律:对于每阶图形,由该阶图形左上角的点,可以推出其他下一阶图形的左上角的点的位置,如果只有一个点了那么就直接赋值为'O'。
#include<bits/stdc++.h> using namespace std; const int MAXN = 2200; int n, row, col;//行和列的值 char a[MAXN][MAXN]; void init()//初始化 { n++;//下面递归中给解释 row = pow(3, n - 1); col = pow(3, n - 1); memset(a, ' ', sizeof(a));//先把所有地方都置为空格 } void draw(int n, int x, int y)//生成图形 { if (n == 1) { a[x][y] = 'O'; return ; } //如果n=0作为结束条件,那么在n=1的时候t=0.333,数组就出错了,所以我让n++且n=1的时候作为结束条件 int t = pow(3, n - 2); draw(n - 1, x , y + t );//上 draw(n - 1, x + t , y );//左 draw(n - 1, x + t , y + 2 * t);//右 draw(n - 1, x + 2 * t, y + t );//下 } void print()//输出 { for (int i = 0; i < row; i++) { for (int j = col - 1; j >= 0; j--) { if(a[i][j] == 'O') { a[i][j + 1] = '\0'; break; }//控制每一行的最后没有空格 } } for (int i = 0; i < row; i++) puts(a[i]); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); init(); draw(n, 0, 0); print(); } return 0; }
H:斐波那契数列
链接:方块与收纳盒
思路:听见他们说是斐波那契数列,然后就敲一发试试,发现数据不超long long,直接A了
#include<bits/stdc++.h> using namespace std; const int MAXN = 85; long long a[MAXN]; void Fib() { a[0] = 1; a[1] = 1; for (int i = 2; i < MAXN; i++) a[i] = a[i - 1] + a[i - 2]; } int main() { Fib(); int T; scanf("%d", &T); while (T--) { int t; scanf("%d", &t); printf("%lld\n", a[t]); } return 0; } /* 3 1 2 4 */
I:暴力
链接:找数字个数
思路:求出先求出a和b数字的每一位数字,放入set。然后暴力判断1~1000,是a和b倍数的直接跳过,分理处当前数字的每一位,如果某一位在set中出现过,也跳过,否则答案++。
#include<bits/stdc++.h> using namespace std; const int MAXN = 85; int main() { int T; scanf("%d", &T); while (T--) { int a, b; scanf("%d%d", &a, &b); int a1 = a, b1 = b; set<int> s; while (a1) { s.insert(a1 % 10); a1 /= 10; } while (b1) { s.insert(b1 % 10); b1 /= 10; } //for (auto i: s) cout << i << " "; cout << endl; long long ans = 0; for (int i = 1; i <= 1000; i++) { if (i % a == 0 || i % b == 0) { continue; }//倍数 int t = i; bool flag = false; while (t) { if (s.count(t % 10)) { flag = true; break; }//含有某一个数字 t /= 10; } if (flag) continue; ans++; } printf("%lld\n", ans); } return 0; } /* 3 2 3 14 23 1234 5678 */
J:规律 + 递归
链接:闯关的lulu
思路:主要是题意,每走一层他会获得一个0,
2个0 == 1个1
3个1 == 1个2
4个2 == 1个3
5个3 == 1个4
。。。
可以推出来:n个k == n/(k-2)个k
他走N层总共会获得N个0,然后我们递归去算有几个几就好了,然后回溯过程种输出。
注意N的奇偶性,如果N为奇数直接在最后输一个0。
#include<bits/stdc++.h> using namespace std; int n; void put(int n, int k) { if (n == 0) return ; put(n / (k + 2), k + 1);//有N / (k + 2)个k+1 n = n % (k + 2);//还有n % (k + 2)个k while (n--) printf("%d", k); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); if (n&1){ put(n - 1, 1); printf("0"); }//奇数个0 else put(n, 1);//偶数个0 printf("\n"); } return 0; }
相关文章推荐
- 2018年全国多校算法寒假训练营练习比赛(第一场)C - 六子冲
- 2018年全国多校算法寒假训练营练习比赛(第一场) - (A,B,F,I,J)
- 2018年全国多校算法寒假训练营练习比赛(第一场)J - 闯关的lulu
- 2018年全国多校算法寒假训练营练习比赛(第一场) Problem:H
- 2018年全国多校算法寒假训练营练习比赛(第一场)C. 六子冲(模拟)
- 2018年全国多校算法寒假训练营练习比赛(第一场) - D - N阶汉诺塔变形(模拟)
- 2018年全国多校算法寒假训练营练习比赛(第一场) - F - 大吉大利,今晚吃鸡——跑毒篇
- 2018年全国多校算法寒假训练营练习比赛(第一场) 大吉大利,今晚吃鸡——枪械篇
- 2018年全国多校算法寒假训练营练习比赛(第一场)
- 2018年全国多校算法寒假训练营练习比赛(第一场)
- 2018年全国多校算法寒假训练营练习比赛(第一场)D. N阶汉诺塔变形(找规律)
- 2018年全国多校算法寒假训练营练习比赛(第一场)C-六子冲(大模拟)
- 2018年全国多校算法寒假训练营练习比赛(第一场) - A - 大吉大利,今晚吃鸡——枪械篇(枚举)
- 2018年全国多校算法寒假训练营练习比赛(第一场)G 圆圈
- 2018年全国多校算法寒假训练营练习比赛(第一场)I
- 2018年全国多校算法寒假训练营练习比赛(第一场) - B - 最强的决斗者一切都是必然的!(模拟)
- 2018年全国多校算法寒假训练营练习比赛(第一场)最强的决斗者一切都是必然的!
- 2018年全国多校算法寒假训练营练习比赛(第一场)G. 圆圈
- 2018年全国多校算法寒假训练营练习比赛(第一场)-I找数字个数
- 2018年全国多校算法寒假训练营练习比赛(第一场) D.N阶汉诺塔变形(数据结构)