目录
总结
- 直接懵逼的题目:
- (题9)连续因子——其实是因为理解错题目
- (题12)搜索树判断,还没怎么学,时间有限也还没补题。
- 看了没敢做的题目:
- (题6)城市间紧急救援:当时没能从签到题的状态切换过来,读完题目直接胆怯了,其实就是最短路的问题,但还是有些棘手的。
- (题11)链表去重:基础知识不完善吧,链表学的不太好,所以看到题目就直接放弃了,看过题解发现还是比较简单的,要巩固基础的知识体系。
- (题14)凑零钱:我知道是搜索问题啊,但是我不会做啊!菜到昏厥。
- 做了但没做全对的题目:
- (题13)肿瘤诊断(满分30拿25):第一反应就是用DFS做,然后不够熟练,中间有很多的问题,debug了很久,终于弄完了发现后两个测试点会爆栈;时间不够了就没用BFS去做。
- (题15)社交集群(满分30拿29):一个刁钻的并查集问题,对人进行处理的话,有一个神奇的测试点不让过,一定要对兴趣进行处理。
- 做了并通过的题目:
- (题1)打印沙漏
- (题2)Hello World
- (题3)计算摄氏温度
- (题4)求整数段和
- (题5)念数字
- (题7)个位数统计
- (题8)考试座位号
- (题10)月饼
靠!这么一看,我做全对的只要签到题。继续加油吧!
1. 打印沙漏
传送门
就是一道简单的找图形规律,然后输出。好在天梯赛的训练没有罚时,不然我对这类题目的解决还是比较慢的。(加减困难)
#include<iostream>
#include<algorithm>
using namespace std;
int n, cnt;
char str;
void get(){
for (int i = 1;; i+=2){
if(i==1 && i<=n){ cnt++;n--;}
else if(2*i<=n){ cnt++;n -= 2 * i;}
else break;
}
}
int main(){
scanf("%d %c", &n, &str);
get();
for (int i = 0; i < cnt; i++){
for (int j = 0; j < i; j++) printf(" ");
for (int j = 0; j < 2 * (cnt-i) - 1; j++) printf("%c", str);
printf("\n");
}
for (int i = cnt - 1; i > 0; i--){
for (int j = 1; j < i; j++) printf(" ");
for (int j = 0; j < 2 * (cnt-i) + 1; j++) printf("%c", str);
printf("\n");
}
printf("%d\n", n);
return 0;
}
2. hello world
3. 计算摄氏温度
4. 求整数段和
5. 念数字
好吧以上都是纯签到题,不附链接和代码了。
6. 紧急救援
传送门
是一道最短路的题目,修改一下Dijkstra的模板就能写。因为连续做了五道签到题,当时看到这个题目的时候,下意识是不敢去做的,然后做了后面的题目也没时间返回来解决这题了。后来补题发现,属实够呛。
跟普通的最短路问题相比,它多了一个人数的问题,即存在多条路径距离相等时,取路过城市救援人数和最多的路径(输入保证救援可行且最优解唯一。)又是第一次碰到这样的限制条件,做了很久,debug也很久,一度对着屏幕发呆怀疑人生)。然后我又意识到,我整理了这么久的最短路问题,最后写的时候,居然忘了怎么输出路径,[菜到哭泣.JPG]。
我补提时的问题都注释在代码中了,菜到昏厥,该长点记性了。
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f3f;
const int num = 505;
struct edge{
int u, v, w, mem;
edge(int a, int b, int c, int d) { u = a;v = b;w = c;mem= d;}
};
vector<edge> e[num];
struct node{
int id, mem, dist;
node(int a, int b, int c) { id = a;dist = b;mem = c;}
bool operator <(const node &x)const{
if(dist==x.dist) return mem < x.mem;
return dist > x.dist;
}
};
//edge 和 node 中都多加了一个数据mem,用来存放该一个城市的救援人数,方便后续的比较
//node中的比较函数也有变化,在存在多条距离相同的路径时,选择救援人数和最多的路径
int n, m, s, d, ans=1;
int mem[num];
int dis[num];
int pre[num];
int nex[num];
//pre和nex,拿来输出路径,我当时真的忘记我自己整理的路径输出print_path了,菜到语塞。
int path[num];
//path统计最短路径的条数,这是这道题我爬的最久的坑
int done[num];
void dijkstra(){
for(int i=0; i<=n; i++){
dis[i] = inf;
done[i] = path[i] = 0;
}
dis[s] = 0;
path[s] = 1;
priority_queue<node> q;
q.push(node(s, dis[s], mem[s]));
while(!q.empty()){
node u = q.top();
q.pop();
if(done[u.id]) continue;
done[u.id] = 1;//最短距离的确定标记依然适用,当时脑昏还把他给扔了
for(int i=0; i<e[u.id].size(); i++){
edge y = e[u.id][i];
if(done[y.v]) continue;
if(dis[y.v] > u.dist+y.w){
dis[y.v] = u.dist + y.w;
mem[y.v] = u.mem + y.mem;
pre[y.v] = u.id;
path[y.v] = path[u.id];
q.push(node(y.v, dis[y.v], mem[y.v]));
}
else if(dis[y.v]==u.dist+y.w){
/* !最重要!*/ path[y.v] += path[u.id];//发现具有相同距离的两条路径之后,最短路数不能简单++!!!
if(mem[y.v] < u.mem+y.mem){
mem[y.v] = u.mem + y.mem;
pre[y.v] = u.id;
q.push(node(y.v, dis[y.v], mem[y.v]));
}
}
}
}
}
int main(){
scanf("%d%d%d%d", &n, &m, &s, &d);
for (int i = 0; i < n; i++) scanf("%d", mem + i);
while(m--){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
e[u].push_back(edge(u, v, w, mem[v]));
e[v].push_back(edge(v, u, w, mem[u]));
}
dijkstra();
int tem = d;
while(tem!=s){
nex[pre[tem]] = tem;
tem = pre[tem];
}
// for (int i = 0; i < n; i++) printf("%d %d\n", i, path[i]);
printf("%d %d\n", path[d], mem[d]);
while(s!=d){
printf("%d ", s);
s = nex[s];
}
printf("%d\n", d);
return 0;
}
7. 个位数统计
水题乱入,大概是队长拉题时拿来安慰我们的吧。
8. 考试座位号
传送门
一道简单的结构体排序,唯一要掌握的就是下面这个函数了。
friend bool operator<(const student &x, const student &y)
#include<iostream>
#include<algorithm>
using namespace std;
int n, m, a[1005];
struct student{
char num[18];
int a, b;
friend bool operator<(const student &x, const student &y){
return x.a < y.a;}
} stu[1005];
int main(){
scanf("%d", &n);
getchar();
for(int i=1; i<=n; i++){
scanf("%s%d%d", stu[i].num, &stu[i].a, &stu[i].b);
getchar();
}
scanf("%d", &m);
for (int i = 0; i < m; i++) scanf("%d", a + i);
// for (int i = 1; i <= n; i++) printf("%s %d %d\n", stu[i].num, stu[i].a, stu[i].b);
sort(stu + 1, stu + n + 1);
// for (int i = 1; i <= n; i++) printf("%s %d %d\n", stu[i].num, stu[i].a, stu[i].b);
for (int i = 0; i < m; i++) printf("%s %d\n",stu[a[i]].num, stu[a[i]].b);
return 0;
}
9. 连续因子
传送门
[强颜欢笑.JPG]当属理解错题目了,然后没思路,就一直没做,直到看了题解,直到读懂题目。[笑.JPG],我****,菜的好真实。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int start, len, cj = 1;
long long n;
int main(){
scanf("%lld", &n);
for(int i=2; i<=sqrt(n); i++){
cj = 1;
for(int j=i; cj*j<=n; j++){
cj = cj*j;
if(n%cj==0 && j-i+1>len){
start = i;
len = j-i+1;
}
}
}
if(start == 0){
len=1;
start=n;
}
cout<<len<<'\n'<<start;
for(int i=start+1;i<start+len;i++){
cout<<'*'<<i;
}
return 0;
}
10. 月饼
也算是水题吧,太常规的题目了,懒得附链接和代码了[狂妄.JPG]。
11. 链表去重
传送门
看题解之前:啊,链表,没怎么学,不会写。试探性地看了看题目,嗯,确实不会写。
看题解之后:呃,就这。其实还是能写的,主要还是没好好学链表,对这个比较陌生。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int num = 100005;
int front, n;
int nex[num];
int vis[num];
int a[num];
int b[num];
struct node{ int key, next;} l[num];
int main(){
scanf("%d%d", &front, &n);
for (int i = 0; i < n; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
l[a].key = b, l[a].next = c;
}
int cnt = 0;
while(front!=-1){
nex[cnt++] = front;
front = l[front].next;
}
int c1 = 0, c2 = 0;
for (int i = 0; i < cnt; i++){
int k = l[nex[i]].key;
if(vis[abs(k)]){
b[c2++] = nex[i];
}
else{
a[c1++] = nex[i];
vis[abs(k)] = 1;
}
}
for (int i = 0; i < c1; i++){
printf("%05d %d ", a[i], l[a[i]].key);
if(i==c1-1) printf("-1\n");
else printf("%05d\n", a[i+1]);
}
for (int i = 0; i < c2; i++){
printf("%05d %d ", b[i], l[b[i]].key);
if(i==c2-1) printf("-1\n");
else printf("%05d\n", b[i+1]);
}
return 0;
}
12. 搜索树判断
传送门
超级重要,搁置了两天才补起来,查看了很多博客,发现这个是最简短的,花了些时间去理解,真的很好。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int manx = 1020;
const int INF = 0x3f3f3f3f;
bool isMirror;
int pre[1010];
vector<int> now;
void f(int l, int r) {
if (l > r) return;
int tr = l + 1;
int tl = r;
if (!isMirror) {
while (tr <= r && pre[tr] < pre[l]) tr++;
while (tl > l && pre[tl] >= pre[l]) tl--;
}
else {
while (tr <= r && pre[tr] >= pre[l]) tr++;
while (tl > l && pre[tl] < pre[l]) tl--;
}
if (tr - tl != 1) return;
f(l + 1, tl);
f(tr, r);
now.push_back(pre[l]);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &pre[i]);
f(0, n - 1);
if (now.size() != n) {
isMirror = 1;
now.clear();
f(0, n - 1);
}
if (now.size() != n) printf("NO");
else {
printf("YES\n%d", now[0]);
for (int i = 1; i < n; ++i)
printf(" %d", now[i]);
}
return 0;
}
13. 肿瘤诊断
传送门
刚学了搜索,来写这个还是够呛,看了练习还不够。用DFS后两个测试点会爆栈,一定要用BFS解决。无非变成了一个三维的空间问题,稍稍修改模板就能用。
然后是一个我和来问我问题的同学都一起犯的问题,存图的数组,开错大小,切记切记!
DFS爆栈代码
#include<iostream>
#include<algorithm>
using namespace std;
int m, n, l, t, s, sum;
int mp[61][1287][129];
int vis[61][1287][129];
int dir[][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
int jud(int i, int j, int k){
if(i<1 || i>l || j<1 || j>m || k<1 || k>n)
return 0;
if(vis[i][j][k] || !mp[i][j][k])
return 0;
return 1;
}
void dfs(int i, int j, int k){
if(s==t) sum += t;
else if(s>t) sum++;
// sum++;
for (int a = 0; a < 6; a++){
int ni = i + dir[a][0];
int nj = j + dir[a][1];
int nk = k + dir[a][2];
if(jud(ni, nj, nk)){
s++;
vis[ni][nj][nk] = 1;
dfs(ni, nj, nk);
}
}
}
int main(){
scanf("%d%d%d%d", &m, &n, &l, &t);
for (int i = 1; i <= l; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= n; k++)
scanf("%d", mp[i][j] + k);
for (int i = 1; i <= l; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= n; k++)
if(!vis[i][j][k] && mp[i][j][k]){
s = 1;
vis[i][j][k] = 1;
dfs(i, j, k);
}
printf("%d\n", sum);
return 0;
}
BFS通过代码
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int sum;
int m, n, l, t;
int mp[70][1300][130];
int vis[70][1300][130];
int dir[][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
struct node{ int x, y, z;};
int jud(int i, int j, int k){
if(i<1 || i>l || j<1 || j>m || k<1 || k>n)
return 0;
if(vis[i][j][k] || !mp[i][j][k])
return 0;
return 1;
}
void BFS(int i, int j, int k){
int s = 1;
queue<node> q;
node front, next;
front.x = i, front.y = j, front.z = k;
q.push(front);
while(!q.empty()){
front = q.front();
q.pop();
for (int a = 0; a < 6; a++){
next.x = front.x + dir[a][0];
next.y = front.y + dir[a][1];
next.z = front.z + dir[a][2];
if(jud(next.x, next.y, next.z)){
s++;
vis[next.x][next.y][next.z] = 1;
q.push(next);
}
}
}
if(s >= t)
sum += s;
}
int main(){
scanf("%d%d%d%d", &m, &n, &l, &t);
for (int i = 1; i <= l; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= n; k++)
scanf("%d", mp[i][j] + k);
for (int i = 1; i <= l; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= n; k++)
if(jud(i, j, k)){
vis[i][j][k] = 1;
BFS(i, j, k);
}
printf("%d\n", sum);
return 0;
}
14. 凑零钱
传送门
又是一道搜索问题,就是不知道怎么处理。注意剪枝!
#include<iostream>
#include<algorithm>
using namespace std;
const int num = 1005;
int n, m, flag;
int a[num];
int b[num];
int c[num];
void dfs(int i, int cnt, int sum){
if(flag || sum+b[i]<m) return;
if(i==n || sum>=m){
if(sum==m){
flag = 1;
printf("%d", c[0]);
for (int i = 1; i < cnt; i++) printf(" %d", c[i]);
printf("\n");
}
return;
}
c[cnt] = a[i];
dfs(i + 1, cnt + 1, sum + a[i]);
dfs(i + 1, cnt, sum);
}
int main(){
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%d", a + i);
sort(a, a + n);
b[n - 1] = a[n - 1];
for (int i = n - 1; i >= 0; i--) b[i] = b[i + 1] + a[i];
dfs(0, 0, 0);
if(!flag) printf("No Solution\n");
return 0;
}
15. 社交集群
传送门
并查集的问题,只是一个人可以有很多很多的爱好,然后要让有相同爱好的人联系起来。我最开始写的代码是对人进行处理的,调整了很多很久,最后还是有一个测试点不过,满分30拿了29,不知道卡在哪个神奇的地方。然后第二种是对兴趣进行处理。
扣了1分的代码
#include<iostream>
#include<algorithm>
using namespace std;
const int num = 1005;
int n, sum;
int hob[num][num], h[num];
int s[num],m[num];
bool cmp(int &a, int &b) { return a > b; }
void init(){
for(int i=0; i<num; ++i) s[i] = i;
}
int find(int x){
return s[x] == x ? x : s[x] = find(s[x]);
}
void sunion(int x, int y){
x = find(x);
y = find(y);
s[y] = x;
}
int main(){
init();
scanf("%d", &n);
for (int i = 1; i <= n; i++){
int a;
scanf("%d:", &a);
while(a--){
int x;
scanf("%d", &x);
hob[x][h[x]++] = i;//i有兴趣x
if(h[x]>1) sunion(i, hob[x][h[x]-2]);
}
}
for (int i = 1; i <= n; i++){
if(s[i]==i) sum++;
m[find(i)]++;
}
printf("%d\n", sum);
sort(m, m + n, cmp);
for (int i = 0; i < sum; i++){
if(i==sum-1) printf("%d\n", m[i]);
else printf("%d ", m[i]);
}
return 0;
}
对兴趣处理的满分代码
#include<iostream>
#include<algorithm>
using namespace std;
const int num = 1005;
int n, sum;
int height[num];
int s[num], mem[num];
bool cmp(int &a, int &b) { return a > b;}
void init(){
for(int i=0; i<num; ++i){
s[i] = i;
height[i] = 0;
}
}
int find(int x){
int r = x;
while(s[r] != r) r = s[r];
int i=x, j;
while(i != r){
j = s[i];
s[i] = r;
i = j;
}
return r;
}
void sunion(int x, int y){
x = find(x);
y = find(y);
if(height[x] > height[y])
s[y] = x;
else{
s[x] = y;
if(height[x] == height[y])
height[y]++;
}
}
int main(){
init();
scanf("%d", &n);
for (int i = 1; i <= n; i++){
int a, t;
scanf("%d:%d", &a, &t);
a--;
while(a--){
int x;
scanf("%d", &x);
sunion(t, x);
}
mem[find(t)]++;
}
for (int i = 1; i < num; i++){
int fi = find(i);
if(mem[i]){
if(i==fi) sum++;
else{
mem[fi] += mem[i];
mem[i] = 0;
}
}
}
sort(mem, mem + num, cmp);
printf("%d\n", sum);
for (int i = 0; i < sum; i++){
if(i==sum-1) printf("%d\n", mem[i]);
else printf("%d ", mem[i]);
}
return 0;
}