C - Digital Graffiti
题目链接: link.
题意:
在一个矩阵中 ‘ . ’ ‘.’ ‘.’代表空,#代表图案,现在矩阵中用#绘制了几个多边形,多边形之间没有相交,矩阵为 n ⋅ m n·m n⋅m的,现在矩阵的中多边形有多少个直边,直边说明没有断开,也没有突起和凹下。
思路:
我的思路是对于图形的上面下面左边右边都判断一下,即几个上面,几个下面,几个左面,几个右面。对于正面来说,就是没有断开,比较暴力的做法,但是很好理解
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n, m;
char s[N][N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
}
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i][j] == '#' && s[i - 1][j] != '#') {
int k;
res++;
for (k = j + 1; k <= m; k++) {
//当前点是‘#’,且不是内部点,必须是最外部的点
if (s[i][k] == '#' && s[i - 1][k] != '#') {
continue;
} else
break;
}
j = k - 1;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i][j] == '#' && s[i + 1][j] != '#') {
res++;
int k;
for (k = j + 1; k <= m; k++) {
//最下面的‘#’点,且不是内部点
if (s[i][k] == '#' && s[i + 1][k] != '#') {
continue;
} else
break;
}
j = k - 1;
}
}
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s[j][i] == '#' && s[j][i - 1] != '#') {
res++;
int k;
for (k = j + 1; k <= n; k++) {
//最左边的‘#’号点
if (s[k][i] == '#' && s[k][i - 1] != '#') {
continue;
} else
break;
}
j = k - 1;
}
}
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s[j][i] == '#' && s[j][i + 1] != '#') {
res++;
int k;
for (k = j + 1; k <= n; k++) {
//最右边的‘#’点
if (s[k][i] == '#' && s[k][i + 1] != '#') {
continue;
} else
break;
}
j = k - 1;
}
}
}
cout << res << endl;
}
另一种思路:
借鉴网上其他大佬的思路,对于多边形是都是直边时,多边形的顶点数=边数,即多边形直角边的数量=边数,是个定理(规律),在此记个笔记。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
char s[N][N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
}
int res = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
int cnt = 0;
if (s[i][j] == '.') cnt++;
if (s[i][j + 1] == '.') cnt++;
if (s[i + 1][j] == '.') cnt++;
if (s[i + 1][j + 1] == '.') cnt++;
if (cnt == 1 || cnt == 3) res++;
}
}
cout << res << endl;
return 0;
}
D - Circle Lattice Points
题目链接: link.
题意:
给一个圆的圆心坐标 ( x , y ) (x,y) (x,y),半径 R R R,现在问你有多少个整数点(坐标的值都是整数值)坐落在圆内或圆上
思路:
坐标的范围给的是
1
0
5
10^5
105肯定不能两重循环枚举每个整数点都判断一遍,跑不了
O
(
n
2
)
O(n^2)
O(n2),需要只跑一遍循化,那么就可以只枚
x
x
x的值,然后求出
y
y
y的范围在范围之内的个数好算,直接
∣
u
p
−
d
o
w
n
+
1
∣
|up-down+1|
∣up−down+1∣,操作起来就是枚举
x
x
x的值,从
x
−
r
x-r
x−r枚举到
x
+
r
x+r
x+r,此时知道了圆心
(
x
,
y
)
(x,y)
(x,y),也知道了枚举的一个点的位置
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),那么此时这个点的上边界和下边界根据勾股定理来求,拿上边界距离,就是
y
i
+
R
2
−
(
x
−
x
i
)
2
y_i+\sqrt{R^2 - (x-x_i)^2}
yi+R2−(x−xi)2就是上边界的值,下边界同理,所以这个
x
i
x_i
xi值下,也就是确定一个竖轴,确定出上下边界,那么上面的点数就是
∣
u
p
−
d
o
w
n
+
1
∣
|up-down+1|
∣up−down+1∣
注意: 这个题比较卡精度,所以我开了
l
o
n
g
d
o
u
b
l
e
long double
longdouble,且半径
R
R
R还加了一个偏移量eps,这个题对于取整问题,可以直接用函数
f
l
o
o
r
floor
floor向上取整和
c
e
i
l
ceil
ceil向下取整
#include <bits/stdc++.h>
using namespace std;
#define eps 1e-14
#define ll long long
int main() {
long double x, y, r;
cin >> x >> y >> r;
r += eps;
ll res = 0;
for (int i = ceil(x - r); i <= floor(x + r); i++) {
long double up, down;
up = y + sqrt(r * r - (x - i) * (x - i));
down = y - sqrt(r * r - (x - i) * (x - i));
res += floor(up) - ceil(down) + 1;
}
cout << res << endl;
}
E - Come Back Quickly
题目链接: link.
题意:
给 n n n个点 m m m条边的单向图, a i a_i ai b i b_i bi c i c_i ci代表 a a a到 b b b的边权为 c c c,现在让你输出从每个点出发,又回到自己的最短路的距离(必须要走),如果走不了,那就输出-1
思路:
这个题给的点数和边数并不多,可以对每个点都跑一遍
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra来求从每个点开始到其他点的最短路,然后再求最短环即可,也就是
r
e
s
=
m
i
n
(
r
e
s
,
f
(
i
,
j
)
+
f
(
j
,
i
)
)
res=min(res,f(i,j)+f(j,i))
res=min(res,f(i,j)+f(j,i)),但这个题有一个细节很恶心,通常我们默认点从自己到自己的最短路一般是0,但是这个题的边可以是自己连接自己,也就是说这里的自环边是要考虑进来的,与平常的最短路不一样。
所以要开个数组,
C
i
r
c
l
e
[
i
]
Circle[i]
Circle[i]存的是从
i
i
i的自环边的最短边
#include <bits/stdc++.h>
using namespace std;
const int N = 2020, M = 4040;
#define inf 0x3f3f3f3f
typedef pair<int, int> PII;
bool vis[N];
int h[N], e[M], ne[M], w[M], idx;
int dis[N], Circle[N];
int f[N][N];
int n, m;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra(int s) {
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s] = 0;
priority_queue<PII, vector<PII>, greater<PII> > q;
q.push({0, s});
while (q.size()) {
auto t = q.top();
q.pop();
int now = t.second, distance = t.first;
if (vis[now]) continue;
vis[now] = 1;
for (int i = h[now]; ~i; i = ne[i]) {
int j = e[i];
if (dis[j] > dis[now] + w[i]) {
dis[j] = dis[now] + w[i];
q.push({dis[j], j});
}
}
}
}
int main() {
memset(h, -1, sizeof(h));
scanf("%d%d", &n, &m);
memset(Circle, 0x3f, sizeof(Circle));
while (m--) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if (a == b) {
Circle[a] = min(Circle[a], c);
continue;
}
add(a, b, c);
}
for (int i = 1; i <= n; i++) {
dijkstra(i);
memcpy(f[i], dis, sizeof(f[i]));
}
for (int i = 1; i <= n; i++) {
int res = inf;
for (int j = 1; j <= n; j++) {
if (i == j) continue;
res = min(res, f[i][j] + f[j][i]);
}
res = min(res, Circle[i]);
if (res == inf) res = -1;
printf("%d\n", res);
}
return 0;
}
F - GCD or MIN
题意:
给
n
n
n个数,从
a
1
a_1
a1
a
2
a_2
a2…
a
n
a_n
an,现在有两种操作、
1.
1.
1.选择两个数
a
i
a_i
ai和
a
j
a_j
aj取
m
i
n
min
min(最小值),并从数组中删除这两个值,把得到的最小值放在数组中。
2.
2.
2.选择两个数
a
i
a_i
ai和
a
j
a_j
aj取
g
c
d
gcd
gcd(最大公约数),并从数组中删除这两个数,把得到的最大公约数放到数组中
一共操作
n
−
1
n-1
n−1次,问最后剩下的数有几种情况
思路:
首先分析直观的
g
c
d
gcd
gcd和
m
i
n
min
min操作,
g
c
d
(
a
,
b
)
<
=
m
i
n
(
a
,
b
)
gcd(a,b)<=min(a,b)
gcd(a,b)<=min(a,b)这个是能简单分析出来的,所以能够确定数组中的最小值一定是答案之一,且其他答案的大小一定
<
<
<数组最小值
也就是说答案一定是如此{a,b,c…z},按照大小排序后,z为所有数都取
m
i
n
min
min的情况,也就是原数组的最小值。而其他数
a
,
b
,
c
.
.
.
a,b,c...
a,b,c...则是小于
z
z
z的最小公约数,那要找到这些数,设为
x
x
x,就会出现原数组的子集
g
c
d
(
a
l
a
l
+
1
.
.
.
a
r
.
.
.
)
gcd(a_la_{l+1}...a_r...)
gcd(alal+1...ar...)
=
=
=
x
x
x,但是不能通过枚举原数组的自己来得到答案,那么反向思考,来枚举因子,看这个因子最后通过多个数取
g
c
d
gcd
gcd后能否得到当前枚举的这个因子,枚举因子的范围一定要小于原数组的最小值。
还有一层分析,所有拥有因子
x
x
x的
a
i
a_i
ai数的
g
c
d
gcd
gcd一定是
x
x
x的倍数
开一个
m
a
p
map
map,
f
[
x
]
f[x]
f[x]存的是因子是x的情况下,能整除
x
x
x的所有的数的
g
c
d
gcd
gcd,最终统计答案的时候,最小值算一个,
f
[
x
]
=
x
f[x]=x
f[x]=x的也算一种,
f
[
x
]
=
x
f[x]=x
f[x]=x说明有多个数取了
g
c
d
gcd
gcd然后又与其他值取了
m
i
n
min
min
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
map<int, int> f;
int n;
int a[N];
set<int> s;
set<int>::iterator it;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
sort(a + 1, a + n + 1);
int res = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= a[i] / j && j < a[1]; j++) {
if (a[i] % j == 0) {
if (!f[j] && j < a[1]) {
f[j] = a[i];
}
if (j < a[1]) {
f[j] = __gcd(f[j], a[i]);
s.insert(j);
}
if (j * j == a[i]) continue;
if (!f[a[i] / j] && a[i] / j < a[1]) {
f[a[i] / j] = a[i];
}
if (a[i] / j < a[1]) {
f[a[i] / j] = __gcd(a[i], f[a[i] / j]);
s.insert(a[i] / j);
}
}
}
}
for (it = s.begin(); it != s.end(); it++) {
cout << *it << " " << f[*it] << endl;
if (f[*it] == *it) {
res++;
}
}
cout << res << endl;
}