756. 蛇形矩阵 (1月11日)
分析
定义方向上右下左, 然后res里放元素即可
联动leetcode54
code
#include <iostream>
#include <vector>
using namespace std;
const int N = 110
int n, m;
int main(){
scanf("%d%d", &n, &m);
int d = 1;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int x = 0, y = 0;
vector<vector<int>> res(n, vector<int>(m));
for (int i = 1; i <= n * m; i ++ ){
res[x][y] = i;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= n || b < 0 || b >= m || res[a][b]){
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;
}
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < m; j ++ )
cout << res[i][j] << ' ';
cout << endl;
}
return 0;
}
1113. 红与黑(1月12日)
分析
洪水灌溉模板题
code(bfs)
#include <iostream>
#include <queue>
using namespace std;
const int N = 30;
int n, m;
char g[N][N];
typedef pair<int, int> PII;
#define x first
#define y second
int bfs(int x, int y){
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
queue<PII> q;
q.push({x, y});
int res = 1;
while (q.size()){
auto t = q.front(); q.pop();
for (int i = 0; i < 4; i ++ ){
int sx = t.x + dx[i], sy = t.y + dy[i];
if (sx >= 0 && sx < n && sy >= 0 && sy < m && g[sx][sy] == '.'){
q.push({sx, sy});
g[sx][sy] = '#';
res ++;
}
}
}
return res;
}
int main(){
while (scanf("%d%d", &m, &n), n || m){
for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);
int x, y;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (g[i][j] == '@') x = i, y = j;
cout << bfs(x, y) << endl;
}
return 0;
}
code(dfs)
递归中res 记录由当前点出发可以扩展到哪些点, 然后返回给上一层
#include <iostream>
#include <queue>
using namespace std;
const int N = 30;
int n, m;
char g[N][N];
typedef pair<int, int> PII;
#define x first
#define y second
int dfs(int x ,int y){
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
g[x][y] = '#';
int res = 1;
for (int i = 0; i < 4; i ++ ){
int sx = x + dx[i], sy = y + dy[i];
if (sx >= 0 && sx < n && sy >= 0 && sy < m && g[sx][sy] == '.') {
res += dfs(sx, sy);
}
}
return res;
}
int main(){
while (scanf("%d%d", &m, &n), n || m){
for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);
int x, y;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (g[i][j] == '@') x = i, y = j;
cout << dfs(x, y) << endl;
}
return 0;
}
1346. 回文平方 (1月13日)
分析
枚举每一个数, 比如x, 先求下 ( x 2 ) b (x^2)_{b} (x2)b, 比如 y = ( x 2 ) b y = (x^2)_{b} y=(x2)b, 如果 y y y是回文数, 那么输出 ( x ) b , ( x 2 ) b (x)_b, (x^2)_b (x)b,(x2)b
进制转换
问题在于如何将
(
x
)
10
(x)_{10}
(x)10转化为b进制的数 ---- 短除法
比如
(
123
)
10
(123)_{10}
(123)10 转化为3进制
对123 不断除以3, 直到商为0位置, 右边是余数
那么转化为3进制后的数为倒着看余数
课堂练习
(
245
)
10
=
(
x
)
5
(245)_{10} = (x)_5
(245)10=(x)5
命题:短除法的正确性
证明:
判断回文
双指针算法, i前指针往后走, j后指针往前走, 每次比较下当前所指的数是否相同, 如果不同的话, 就不是回文数, 如果相同, 则继续往中间靠拢
扩展
- 10进制转化为其他进制 — 短除法
- 其他进制转化成10进制 ? 直接扫描一遍做
( 11101 ) 2 (11101)_2 (11101)2
= 1 ∗ 2 4 + 1 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 =1*2^4 + 1* 2^3 + 1* 2^2 + 0 * 2^1 + 1* 2^0 =1∗24+1∗23+1∗22+0∗21+1∗20
但是写代码的时候, 一般不这么写, 因为你需要先算下
2
4
,
2
3
,
2
2
,
2
1
,
2
0
2^4, 2^3, 2^2, 2^1, 2^0
24,23,22,21,20, 算的次数比较多
秦九韶算法
比如算
a
n
x
n
+
a
n
−
1
x
n
−
1
+
.
.
.
+
a
0
x
0
a_nx^n + a_{n - 1}x^{n - 1} + ... + a_0x_0
anxn+an−1xn−1+...+a0x0
先算
a
n
a_n
an, 第2次算
a
n
x
+
a
n
−
1
a_nx + a_{n - 1}
anx+an−1, 然后第3次算
(
a
n
x
+
a
n
−
1
)
∗
x
+
a
n
−
2
(a_nx + a_{n - 1}) * x + a_{n - 2}
(anx+an−1)∗x+an−2, 第4次
(
(
a
n
x
+
a
n
−
1
)
∗
x
+
a
n
−
2
)
∗
x
+
a
n
−
3
((a_nx + a_{n - 1}) * x + a_{n - 2})* x + a_{n - 3}
((anx+an−1)∗x+an−2)∗x+an−3, 依此类推, 做n + 1次, 就可以计算得到结果
发现只要做n次加法, n次乘法就可以了, 不需要预处理指数的问题
例子:
(
11101
)
2
(11101)_2
(11101)2
计算第2位的时候:
1
∗
2
+
1
=
3
1 * 2 + 1 = 3
1∗2+1=3
计算第3位的时候:
3
∗
2
+
1
=
7
3 * 2 + 1 = 7
3∗2+1=7
计算第4位的时候:
7
∗
2
+
0
=
14
7 * 2 + 0 = 14
7∗2+0=14
计算第5位的时候:
14
∗
2
+
1
=
29
14 * 2 + 1 = 29
14∗2+1=29
代码:
3. 如何将其他进制转化为其他进制(a进制转化为b进制)
最简单的做法 a —> 10 —> b
也可以直接用短除法做
联动题
acwing 124.进制转换
code
#include <iostream>
#include <algorithm>
using namespace std;
char get(int x){ // 如果高于10进制, 转化成A
if (x <= 9) return x + '0';
return x - 10 + 'A';
}
string base(int n, int b){ // 短除法
string num;
while (n) num += get(n % b), n /= b;
reverse(num.begin(), num.end());
return num;
}
bool check(string num){
for (int i = 0, j = num.size() - 1; i < j; i ++, j -- )
if (num[i] != num[j]) return false;
return true;
}
int main(){
int b;
cin >> b;
for (int i = 1; i <= 300; i ++ ){
auto num = base(i * i, b); // i * i 的 b 进制表示
if (check(num)){
cout << base(i, b) << ' ' << num << endl;
}
}
return 0;
}
680. 剪绳子(1月14日)
分析
转换下思想, 如果告诉我们已经切出来x长度, 然后问n根绳子一共可以切出多少条长度为x的, 那就比较容易了
比方说第1条绳子可以裁出1条长度为x的绳子, 第2条绳子可以裁出2条长度为x的绳子, 第3条绳子可以裁出1条长度为x的绳子, 总共可以裁出4条长度为x的绳子, 所以长度为x的绳子, 我们最多可以裁出4条
所以发现直接求最优解不好求, 但是已知x, 让我们求可以裁出多少根的话, 就比较容易了
考虑能不能用二分, 可以帮助我们将 最优化问题->判定问题
判定问题比原来的问题多了一个额外的条件x
求当前绳子能够裁剪出多少条长度为x的绳子:
⌊
L
i
x
⌋
\lfloor \frac{L_i}{x} \rfloor
⌊xLi⌋ , 然后遍历下每根绳子, 求和
二分之前, 先考虑下能不能二分(即判断mid无论何种情况, 能否将区间缩小一半)
假如二分中点mid成立, 那么答案区间在[mid, R]
mid不成立, 说明答案一定不在右边, 因为mid⬆️
∑
⌊
L
i
m
i
d
⌋
\sum \lfloor \frac{Li}{mid} \rfloor
∑⌊midLi⌋, 会更得更小, 因此答案所在区间变成[L, mid]
因此, 不管怎样, 答案所在的区间长度都会/2, 不断/2, 区间长度变得很小, 直到小于eps
code
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
bool check(double x){
int cnt = 0;
for (int i = 0; i < n; i ++ )
cnt += w[i] / x;
return cnt >= m;
}
int main(){
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> w[i];
double l = 0, r = 1e9;
while (r - l > 1e-4){
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.2lf\n", l);
return 0;
}
1227. 分巧克力(1月15日)
分析
二分长度即可
code
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, k;
bool check(int x){
int res = 0;
for (int i = 0; i < n; i ++ ){
res += (a[i] / x) * (b[i] / x);
}
return res >= k;
}
int main(){
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++ ){
scanf("%d%d", &a[i], &b[i]);
}
int l = 1, r = N;
while (l < r){
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
printf("%d\n", l);
return 0;
}
422. 校门外的树(1月16日)
分析
题目给了总区间 求除去给定另外一段区间后, 剩余的点的数量
直接扫描一遍给的区间, 标记下, 然后再扫描一遍总区间, 对没打标记的点统计下个数, 就可以了
code
#include <iostream>
using namespace std;
const int N = 100010;
bool st[N];
int res;
int l, m;
int main(){
cin >> l >> m;
while (m -- ){
int a, b;
scanf("%d%d", &a, &b);
for (int i = a; i <= b; i ++ ) st[i] = 1;
}
for (int i = 0; i <= l; i ++ ) res += !st[i];
cout << res << endl;
return 0;
}
429. 奖学金(1月17日)
分析
注意排序的写法
错误写法(要重视)
struct Stu{
int a, b, c, sum, id;
bool operator < (Stu &W){
return sum > W.sum;
if (sum == W.sum) return a > W.a;
if (a == W.a) return id < W.id;
}
}stu[N];
这样写sum == W.sum
的时候会在第1个语句sum > W.sum
返回0, 无法继续判断
code
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 310;
struct Person{
int id, sum, a, b, c;
bool operator < (const Person &w){
if (sum != w.sum) return sum > w.sum;
if (a != w.a) return a > w.a;
return id < w.id;
}
}p[N];
int n;
int main(){
cin >> n;
for (int i = 1; i <= n; i ++) {
int a, b, c;
cin >> a >> b >> c;
p[i] = {i, a + b + c, a, b, c};
}
sort(p + 1, p + n + 1);
for (int i = 1; i <= 5; i ++) cout << p[i].id << ' ' << p[i].sum << endl;
return 0;
}