1.日志统计
小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N 行。
其中每一行的格式是:
ts id
表示在 ts 时刻编号 id 的帖子收到一个”赞”。
现在小明想统计有哪些帖子曾经是”热帖”。
如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是”热帖”。
具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是”热帖”。
给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。
输入格式
第一行包含三个整数 N,D,K。
以下 N 行每行一条日志,包含两个整数 ts 和 id。
输出格式
按从小到大的顺序输出热帖 id。
每个 id 占一行。
数据范围
1≤K≤N≤105,
0≤ts,id≤105,
1≤D≤10000
输入样例:
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
输出样例:
1
3
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N = 100010;
int n, d,k;
PII logs[N];//记录时间与id
int cnt[N];//记录id出现次数
bool st[N];//记录每个帖子是否是热帖
int main()
{
scanf("%d%d%d", &n, &d, &k);
for(int i = 0; i < n; i++) scanf("%d%d", &logs[i].x, &logs[i].y);
sort(logs,logs + n);//按照时间排序
for(int i = 0, j = 0; i < n; i++)
{
int id = logs[i].y;//新加帖子的id
cnt[id]++;
while(logs[i].x - logs[j].x >= d) //时间跨度
{
cnt[logs[j].y]--;
j++;
}
if(cnt[id] >= k) st[id] = true;//如果当前这个id的赞大于等于k,标记为是热帖
}
for(int i = 0;i <= 100000;i++)//遍历id
if(st[i])
printf("%d\n", i);
return 0;
}
2.献给阿尔吉侬的花束
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 210;
int n, m;
char g[N][N];
int d[N][N];
int bfs(PII start, PII end)
{
queue<PII> q;//队列
memset(d, -1, sizeof d);//将距离初始化为-1
d[start.x][start.y] = 0;//起点的距离为0
q.push(start);//将起点放到队列中
int dx[4] = {-1,0,1,0}, dy[4] = {0,1,0,-1};
while(q.size())
{
auto t = q.front();//队头
q.pop();//队头元素出队
for(int i = 0; i < 4; i++)//遍历四个方向
{
int x = t.x + dx[i], y = t.y + dy[i];
if(x < 0 || x >= n || y <0 || y >= m || g[x][y] == '#' || d[x][y] != -1) continue;//出界 或者不能走 或者 已走过
//如果可以走,更新距离
d[x][y] = d[t.x][t.y] + 1;
if(end == make_pair(x, y)) return d[x][y];//返回到终点的距离
q.push({x, y});
}
}
return -1;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for(int i = 0; i <n; i++) scanf("%s", g[i]);
PII start, end;//起点 终点
for(int i = 0; i <n; i++)
for(int j = 0; j < m; j++)
if(g[i][j] == 'S') start = {i, j};
else if(g[i][j] == 'E') end = {i, j};
int distance = bfs(start, end);
if(distance == -1) puts("oop!");
else printf("%d\n",distance );
}
return 0;
}
3.红与黑
AcWing1113. 红与黑
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。
你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。
请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
输入格式
输入包括多个数据集合。
每个数据集合的第一行是两个整数 W 和 H,分别表示 x 方向和 y 方向瓷砖的数量。
在接下来的 H 行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
输出格式
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
数据范围
1≤W,H≤20
输入样例:
6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0
输出样例:
45
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 30;
int w, h;
char g[N][N];
bool st[N][N];
int ans;
int dfs(int x, int y)
{
int cnt = 1;
int dx[4] = {-1, 0, 1,0}, dy[4] = {0,1,0,-1};
st[x][y] = true;
for(int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y +dy[i];
if(a >= 0 && b >= 0 && a < h && b < w && g[a][b] == '.' && !st[a][b]) cnt += dfs(a, b);//不需要回溯
}
return cnt;
}
int main()
{
int x, y;
while(cin >> w >> h && w || h)//输入判断
{
for(int i = 0;i < h; i++) cin >> g[i];
for(int i = 0; i < h ; i++)
for(int j = 0; j <w; j++)
if(g[i][j] == '@')//找起点
{
x = i;
y = j;
}
memset(st, 0, sizeof st);
cout << dfs(x, y)<< endl;
}
return 0;
}
4.交换瓶子
AcWing1224. 交换瓶子
有 N 个瓶子,编号 1∼N,放在架子上。
比如有 5 个瓶子:
2 1 3 5 4
要求每次拿起 2 个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换 2 次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式
第一行包含一个整数 N,表示瓶子数量。
第二行包含 N 个整数,表示瓶子目前的排列状况。
输出格式
输出一个正整数,表示至少交换多少次,才能完成排序。
数据范围
1≤N≤10000,
输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2
将瓶子与对应的位置连接起来
- 情况1:交换同一个环内的点——>裂成两个环
- 情况2:交换不同环中的点——>合并两个环
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 10010;
int n;
int b[N];
bool st[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
int cnt = 0;
//统计环的数量
for(int i = 1; i <= n; i++)
if(!st[i])//如果当前这个点没有被找过
{
cnt++;//说明这个点在一个新的环里面
for(int j = i; !st[j]; j = b[j])//标记
st[j] = true;
}
printf("%d\n", n - cnt);
return 0;
}
5. 完全二叉树的权值
AcWing 1240. 完全二叉树的权值
给定一棵包含 N 个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是 A1,A2,⋅⋅⋅AN,如下图所示:
现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?
如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是 1。
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,⋅⋅⋅AN。
输出格式
输出一个整数代表答案。
数据范围
1≤N≤105,
−105≤Ai≤105
输入样例:
7
1 6 5 4 3 2 1
输出样例:
2
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, a[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
LL maxs = -1e18;
int depth = 0;
for(int d = 1, i = 1; i <= n; i*=2, d++)//循坏遍历每层,d表示层数,i表示每层第一个节点的下标
{
LL s = 0;
for(int j = i; j < i + (1<<d -1) && j <= n ; j++)//循环每层,求和,找规律
s += a[j];
if(s >maxs)
{
maxs = s;
depth = d;
}
}
printf("%d\n", depth);
return 0;
}
6.地牢大师
AcWing1096. 地牢大师
你现在被困在一个三维地牢中,需要找到最快脱离的出路!
地牢由若干个单位立方体组成,其中部分不含岩石障碍可以直接通过,部分包含岩石障碍无法通过。
向北,向南,向东,向西,向上或向下移动一个单元距离均需要一分钟。
你不能沿对角线移动,迷宫边界都是坚硬的岩石,你不能走出边界范围。
请问,你有可能逃脱吗?
如果可以,需要多长时间?
输入格式
输入包含多组测试数据。
每组数据第一行包含三个整数 L,R,C 分别表示地牢层数,以及每一层地牢的行数和列数。
接下来是 L 个 R 行 C 列的字符矩阵,用来表示每一层地牢的具体状况。
每个字符用来描述一个地牢单元的具体状况。
其中, 充满岩石障碍的单元格用”#”表示,不含障碍的空单元格用”.”表示,你的起始位置用”S”表示,终点用”E”表示。
每一个字符矩阵后面都会包含一个空行。
当输入一行为”0 0 0”时,表示输入终止。
输出格式
每组数据输出一个结果,每个结果占一行。
如果能够逃脱地牢,则输出”Escaped in x minute(s).”,其中X为逃脱所需最短时间。
如果不能逃脱地牢,则输出”Trapped!”。
数据范围
1≤L,R,C≤100
输入样例:
3 4 5
S....
.###.
.##..
###.#
#####
#####
##.##
##...
#####
#####
#.###
####E
1 3 3
S##
#E#
###
0 0 0
输出样例:
Escaped in 11 minute(s).
Trapped!
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
struct Point
{
int x,y,z;
};
const int N = 110;
int L, R, C;
char g[N][N][N];
Point q[N*N*N];
int d[N][N][N];
int dx[6] = {0, 1, 0, -1, 0, 0}; //东南西北上下
int dy[6] = {1, 0, -1, 0, 0, 0};
int dz[6] = {0, 0, 0, 0, 1, -1};
int bfs(Point start, Point end)
{
int hh = 0, tt = 0;
q[0] = start;
memset(d, -1,sizeof d);
d[start.x][start.y][start.z] = 0;
while(hh <= tt)
{
auto t = q[hh++];
for(int i = 0;i < 6; i++)
{
int x = t.x + dx[i] , y = t.y + dy[i] , z = t.z + dz[i];
if(x < 0 || x >= L || y < 0 || y >= R || z < 0 || z >= C) continue;
if(g[x][y][z] == '#') continue;
if(d[x][y][z] != -1) continue;
d[x][y][z] = d[t.x][t.y][t.z] + 1;
if(x == end.x && y == end.y && z == end.z) return d[x][y][z];
q[++ tt] = {x,y,z};
}
}
return -1;
}
int main()
{
while(scanf("%d%d%d", &L, &R, &C), L||R||C)
{
Point start, end;
for(int i = 0; i < L; i++)
for(int j = 0; j < R; j++)
{
scanf("%s", g[i][j]);
for(int k = 0; k < C;k++)
{
char c = g[i][j][k];
if(c == 'S') start = {i,j,k};
else if(c == 'E') end = {i, j, k};
}
}
int distance = bfs(start, end);
if(distance == -1) printf("Trapped!\n");
else printf("Escaped in %d minute(s).\n", distance);
}
return 0;
}
7.全球变暖
AcWing1233. 全球变暖
你有一张某海域 N×N 像素的照片,”.”表示海洋、”#”表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中”上下左右”四个方向上连在一起的一片陆地组成一座岛屿,例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入格式
第一行包含一个整数N。
以下 N 行 N 列,包含一个由字符”#”和”.”构成的 N×N 字符矩阵,代表一张海域照片,”#”表示陆地,”.”表示海洋。
照片保证第 1 行、第 1 列、第 N 行、第 N 列的像素都是海洋。
输出格式
一个整数表示答案。
数据范围
1≤N≤1000
输入样例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出样例1:
1
输入样例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
输出样例2:
1
算法分析
遍历所有未遍历过的陆地,通过bfs计算出当前位置连通陆地的数量total,以及被淹没陆地的数量bound,若total == bound表示完整淹没的一个岛屿
- 1.算出有多少个连通块
- 2.多少个会被淹没 一共有多少个岛屿连通,边界与海相连的会被淹没
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n;
char g[N][N];
bool st[N][N];
PII q[N*N];
int dx[4] = {-1,0,1,0}, dy[4] = {0,1,0,-1};
void bfs(int x, int y, int &total, int &bound)
{
int hh = 0, tt = 0;
q[0] = {x, y};
st[x][y] = true;
while(hh <= tt)
{
PII t = q[hh++];
total ++;
bool is_bound = false;
for(int i = 0; i < 4; i++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || b < 0|| a >= n || b >= n) continue;//出界
if(st[a][b]) continue;
if(g[a][b] == '.')//如果是大海
{
is_bound = true;//这个块的与海相连
continue;
}
q[++tt] = {a, b};
st[a][b] = true;
}
if(is_bound) bound++;
}
}
int main()
{
cin >> n;
for(int i = 0; i < n;i++) scanf("%s", g[i]);
int cnt = 0;//所有被淹没岛屿的个数
for(int i = 0; i <n; i++)
for(int j = 0; j < n; j++)
if(!st[i][j] && g[i][j] == '#')//如果当前这个格子没有被搜过并且是岛屿
{
int total = 0, bound = 0;//所有单元和边界的数量
bfs(i, j, total, bound);
if(total == bound) cnt++;//如果所有单元都在边界上,被淹没的岛屿数量++
}
printf("%d\n", cnt);
return 0;
}
8.大臣的旅费
AcWing1207. 大臣的旅费
很久以前,T王国空前繁荣。
为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。
同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。
所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。
他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数 n,表示包括首都在内的T王国的城市数。
城市从 1 开始依次编号,1 号城市为首都。
接下来 n−1 行,描述T国的高速路(T国的高速路一定是 n−1 条)。
每行三个整数 Pi,Qi,Di,表示城市 Pi 和城市 Qi 之间有一条双向高速路,长度为 Di 千米。
输出格式
输出一个整数,表示大臣J最多花费的路费是多少。
数据范围
1≤n≤105,
1≤Pi,Qi≤n,
1≤Di≤1000
输入样例:
5
1 2 2
1 3 1
2 4 5
2 5 4
输出样例:
135
分析:
由于题目说到不重复经过大城市,从首都到达每个大城市的方案都是唯一的。因此可以知道该图是一棵树,本题求的是树的直径
树的直径:树中长度最长的路径
-
1、任取一点x
-
2、找到距离x最远的点y
-
3、从y开始遍历,找到离y最远的点,与y最远的点的距离是树的直径
dfs
1、通过深度优先遍历找到与x的最远距离的点y
2、再通过深度优先遍历找到与y的最远距离
注意:递归函数需要记录上一结点father
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 100010;
int n;
int d[N];
struct Edge
{
int id, w;
};
vector<Edge> h[N];
void dfs(int u, int father, int dist)
{
d[u] = dist;
for(auto node : h[u])//遍历当前节点的所有连接点
if(node.id != father) //不往回走的话,继续遍历
{
dfs(node.id, u, dist + node.w);
}
}
int main()
{
cin >> n ;
memset(d,-1,sizeof d);
for(int i = 0; i <n - 1; i++)
{
int a,b,c;
scanf("%d%d%d", &a, &b, &c);
h[a].push_back({b,c});//无向图
h[b].push_back({a,c});
}
dfs(1,-1,0);
//找到距离1号点最远的一个点
int u = 1;
for(int i = 1; i <= n; i++)
if(d[i] > d[u]) u = i;
dfs(u, -1, 0);
for(int i = 1; i <= n; i++)
if(d[i] >d[u]) u = i;
int s = d[u];
printf("%lld\n", s*10 + s*(s +1ll)/2);//费用的公式
return 0;
}