AtCoder Beginner Contest 191

Posted 佐鼬Jun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 191相关的知识,希望对你有一定的参考价值。

C - Digital Graffiti

题目链接: link.

题意:

在一个矩阵中 ‘ . ’ ‘.’ .代表空,#代表图案,现在矩阵中用#绘制了几个多边形,多边形之间没有相交,矩阵为 n ⋅ m n·m nm的,现在矩阵的中多边形有多少个直边,直边说明没有断开,也没有突起和凹下。

思路:

我的思路是对于图形的上面下面左边右边都判断一下,即几个上面,几个下面,几个左面,几个右面。对于正面来说,就是没有断开,比较暴力的做法,但是很好理解

#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| updown+1,操作起来就是枚举 x x x的值,从 x − r x-r xr枚举到 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(xxi)2 就是上边界的值,下边界同理,所以这个 x i x_i xi值下,也就是确定一个竖轴,确定出上下边界,那么上面的点数就是 ∣ u p − d o w n + 1 ∣ |up-down+1| updown+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] 以上是关于AtCoder Beginner Contest 191的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Beginner Contest 234

AtCoder Beginner Contest 115 题解

AtCoder Beginner Contest 154 题解

AtCoder Beginner Contest 103

AtCoder Beginner Contest 228

AtCoder Beginner Contest 242