Chapter6 双指针,BFS和图论
-
双指针
1.日志统计 1238
经典的双指针模板题
所谓双指针其实就是针对多重循环的一种优化方式,缩小时间复杂度以确保不会TLE
循环的是一个时间段
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, d, k;
bool st[N];
int cnt[N];
PII rec[N];
int main()
{
cin >> n >> d >> k;
for(int i = 0; i < n; i++ ) scanf("%d%d", &rec[i].x, &rec[i].y);
sort(rec, rec + n);
for(int i = 0, j = 0; i < n; i++ )
{
int id = rec[i].y;
cnt[id]++;
while(rec[i].x - rec[j].x >= d) cnt[rec[j].y]--, j++;//优化循环
if(cnt[id] >= k) st[id] = true;
}
for(int i = 0; i <= N; i++ )
if(st[i]) cout << i << endl;
return 0;
}
-
BFS
1.献给阿尔吉侬的花束 1101
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 210;
char a[N][N];
int dist[N][N];
int r, c;
int bfs(PII start, PII end)
{
queue<PII> q;
memset(dist, -1, sizeof dist);
dist[start.x][start.y] = 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 >= r || y < 0 || y >= c) continue;
if(dist[x][y] != -1) continue;
if(a[x][y] == '#') continue;
dist[x][y] = dist[t.x][t.y] + 1;
if(end == make_pair(x, y)) return dist[x][y];
q.push({x, y});
}
}
return -1;
}
int main()
{
int T;
cin >> T;
while(T--)
{
PII start, end;
cin >> r >> c;
for(int i = 0; i < r; i++ ) cin >> a[i];
for(int i = 0; i < r; i++ )
for(int j = 0; j < c; j++ )
{
if(a[i][j] == 'S') start = {i, j};
if(a[i][j] == 'E') end = {i, j};
}
int distance = bfs(start, end);
if(distance == -1) puts("oop!");
else cout << distance << endl;
}
return 0;
}
-
图论
1.交换瓶子 1224
找原状态下有多少个环,单独的数自己成为一个环。每次交换同一个环内的数总环的数量增加1,所以想要达到题目要求的样子需要交换n - 初始环数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
bool st[N];
int n, b[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i++ ) scanf("%d", b + i);
int rnd = 0;
for(int i = 1; i <= n; i++ )
if(!st[i])
{
rnd++;
for(int j = i; !st[j]; j = b[j]) st[j] = true;
}
cout << n - rnd << endl;
return 0;
}
DFS + Floodfill
红与黑 1113
dfs实现floodfill,依次遍历每个点的周围的点然后dfs邻近的点
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25;
char a[N][N];
bool st[N][N];
int w, h;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(int x, int y)
{
int cnt = 1;
st[x][y] = true;
for(int i = 0; i < 4; i++ )
{
int m = x + dx[i], n = y + dy[i];
if(m < 0 || m >= h || n < 0 || n >= w) continue;
if(a[m][n] == '#') continue;
if(st[m][n]) continue;
cnt += bfs(m, n);
}
return cnt;
}
int main()
{
while(cin >> w >> h, w || h)//w列 h行
{
int x, y;
for(int i = 0; i < h; i++ ) scanf("%s", a[i]);
for(int i = 0; i < h; i++ )
for(int j = 0; j < w; j++ )
if(a[i][j] == '@')
{
x = i;
y = j;
}
memset(st, 0, sizeof st);
cout << bfs(x, y) << endl;
}
return 0;
}
-
习题
1.完全二叉树的权值 1240 (双指针)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, a[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i++ ) scanf("%d", a + i);
LL maxn = -1e18;
int rnd;
for(int i = 1, d = 1; i <= n; i *= 2, d++ )
{
LL s = 0;
for(int j = i; j <= n && j < i + (1 << d - 1); j++ )
s += a[j];
if(s > maxn)
{
rnd = d;
maxn = s;
}
}
cout << rnd << endl;
return 0;
}
2.地牢大师 1096
花束的推广,三维的bfs floodfill就可以解决,较简单
比较巧妙的是bfs函数中 ss 与 ee。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Point{
int x, y, z;
};
const int N = 110;
char g[N][N][N];
Point q[N * N * N];
int dist[N][N][N];
int L, R, C;
int dx[6] = {0, 0, -1, 0, 1, 0};
int dy[6] = {0, 0, 0, 1, 0, -1};
int dz[6] = {1, -1, 0, 0, 0, 0};
int bfs(Point start, Point end)
{
memset(dist, -1, sizeof dist);
dist[start.x][start.y][start.z] = 0;
int ss = 0, ee = 0;
q[0] = start;//根节点入列
while(ss <= ee)
{
auto t = q[ss++];
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(dist[x][y][z] != -1) continue;
dist[x][y][z] = dist[t.x][t.y][t.z] + 1;
if(x == end.x && y == end.y && z == end.z) return dist[x][y][z];
q[++ee] = {x, y, z};
}
}
return -1;
}
int main()
{
while(cin >> 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++ )
{
if(g[i][j][k] == 'S') start = {i, j, k};
if(g[i][j][k] == 'E') end = {i, j, k};
}
}
int distance = bfs(start, end);
if(distance == -1) printf("Trapped!
");
else printf("Escaped in %d minute(s).
", distance);
}
return 0;
}
3.全球变暖 1233(bfs floodfill dfs)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char g[N][N];
bool st[N][N];
PII q[N * N];
int n;
void bfs(int x, int y, int &total, int &bound)
{
st[x][y] = true;
q[0] = {x, y};
int ss = 0, ee = 0;
while(ss <= ee)
{
PII t = q[ss++];
total++;
bool is_bound = false;
for(int i = 0; i < 4; i++ )
{
int pp = t.x + dx[i], qq = t.y + dy[i];
if(pp < 0 || pp >= n || qq < 0 || qq >= n) continue;
if(st[pp][qq]) continue;
if(g[pp][qq] == '.')
{
is_bound = true;
continue;
}
q[++ee] = {pp, qq};
st[pp][qq] = true;
}
if(is_bound) bound++;
}
}
int main()
{
scanf("%d", &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(g[i][j] == '#' && !st[i][j])
{
int total = 0, bound = 0;
bfs(i, j, total, bound);
if(total == bound) cnt++;
}
cout << cnt << endl;
return 0;
}
4.大臣的旅费 1207(待补充单链表法)
找树的直径:
任取一点x,找距离x最远的点y
再找距离y最远的点z
yz距离即为数的直径
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
struct Node
{
int id, w;
};
int dist[N];
int n;
vector<Node> h[N];
void dfs(int u, int father, int distance)
{
dist[u] = distance;
for(auto node : h[u])
if(node.id != father)//防止dfs回去
dfs(node.id, u, node.w + distance);
}
int main()
{
cin >> n;
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);
int u = 1;
for(int i = 1; i <= n; i++ )
if(dist[i] > dist[u])
u = i;
dfs(u, -1, 0);//无父节点,形参二任取,取-1
for(int i = 1; i <= n; i++ )
if(dist[i] > dist[u])
u = i;
int s = dist[u];
printf("%lld
", s * 10 + s * (s + 1ll) / 2);
return 0;
}
-
单链表
1.单链表 826
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int head, e[N], ne[N], idx;
//链表初始化
void init()
{
head = -1;
idx = 0;
}
void add_head(int x)
{
e[idx] = x;
ne[idx] = head;
head = idx++;
}
void add_k(int k, int x)
{
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx ++ ;
}
void remove(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
init();
int m;
cin >> m;
while (m -- )
{
char op;
int k, x;
cin >> op;
if (op == 'H')
{
cin >> x;
add_head(x);
}
else if (op == 'I')
{
cin >> k >> x;
add_k(k - 1, x);
}
else
{
cin >> k;
if (!k) head = ne[head];
else remove(k - 1);
}
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
cout << endl;
return 0;
}