1.最短距离
题目简化:
明确问题 + 算法提示:
1.如何判断同类之间的最短距离为0 ---> 并查集+路径压缩
2.如何存储任意两类的距离 ---> 邻接矩阵存储无向图
3.如何表示每个点属于哪一类 ---> 用数组id[节点]存储属于哪一类
4.如何算出任意两类之间的最短距离 ---> 最短路问题:Floyd算法(多源汇最短路,多个起点)[动态规划]
详细思路:
并查集+路径压缩
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
Floyd算法
for (int t = 1; t <= k; t ++)
{
for (int i = 1; i <= k; i ++)
{
for (int j = 1; j <= k; j ++)
{
d[i][j] = min(d[i][j], d[i][t] + d[t][j]);
}
}
}
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = 510, INF = 0x3f3f3f3f;
int p[N], id[N];
int d[M][M]; //两个类之间的距离
int n, m, k;
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
bool check()
{
for (int i = 2; i <= n; i ++)
if (id[i] == id[i - 1] && find(i) != find(i - 1))
return false;
return true;
}
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i ++) p[i] = i;
for (int i = 1, j = 1; i <= k; i ++)
{
int c;
cin >> c;
while (c --) id[j ++] = i;
}
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= k; i ++ ) d[i][i] = 0;
for (int i = 1; i <= m; i ++)
{
int u, v, x;
cin >> u >> v >> x;
if (!x) p[find(u)] = find(v);
int a = id[u], b = id[v];
d[a][b] = d[b][a] = min(d[a][b], x);
}
if (!check()) puts("No");
else
{
puts("Yes");
for (int t = 1; t <= k; t ++)
{
for (int i = 1; i <= k; i ++)
{
for (int j = 1; j <= k; j ++)
{
d[i][j] = min(d[i][j], d[i][t] + d[t][j]);
}
}
}
for (int i = 1; i <= k; i ++)
{
for (int j = 1; j <= k; j ++)
{
if (d[i][j] == INF) d[i][j] = -1;
cout << d[i][j] << ' ';
}
cout << endl;
}
}
}
2.奶牛报数
题目简化:
将这n头牛按顺时针围成一个圈,然后选定一头牛开始从1报数,所有的牛报完数,每一头牛都有相应的报的数字,当报到的数落在[l, r)区间中,则表示该牛被选中制作牛肉
限制条件:所选牛的重量总和最大且第一头牛所报的数尽量小的方案
求:第一头牛所报的数
明确问题 + 算法提示:
1.如何表示按顺时针围成一圈的牛?---> 破环成链
2.如何算出报数在区间内牛的重量总和?---> 区间和:前缀和
3.如何算出第一头牛所报的数? ---> 通过第i头牛报的数,算出第1头牛所报的数
图解分析:
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, l, r;
int a[2 * N], s[2 * N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
a[i + n] = a[i];
}
//求前缀和
for (int i = 1; i <= 2 * n; i ++) s[i] = s[i - 1] + a[i];
cin >> l >> r;
int len = r - l, res = -1, ans = 0;
for (int i = 1; i <= 2 * n - len; i ++)
{
int sum = s[i + len - 1] - s[i - 1], idx = l - i + 1;
while (idx < 1) idx += n;
if (sum > ans || sum == ans && idx < res)
{
res = idx;
ans = sum;
}
}
cout << res << endl;
}
3.机器人跳跃问题
题目简化:
机器人初始有能量值e,机器人需要跳跃n个建筑,每个建筑有对应的高度,第i个建筑的高度
为h[i],机器人每次跳跃当前建筑,所花费的代价为e - h[i](即当e > h[i]时,能量增加e - h[i];当e < h[i]时,能量减少e - h[i]), 在每次跳跃后要保证能量值不能为负数
图示分析:
算法:递推 + 二分查找
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, h[N];
bool judge(int e)
{
for (int i = 1; i <= n; i ++)
{
e = 2 * e - h[i];
if (e < 0) return false;
if (e >= N) return true;
}
return true;
}
int main()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i ++) scanf ("%d", &h[i]);
int l = 0, r = N;
while (l < r)
{
int mid = (l + r) / 2;
if (judge(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
return 0;
}
注意:题目均来自AcWing
所有笔记总结目录-CSDN博客