AtCoder Beginner Contest 218(补题)

C - Shapes

C - Shapes
在这里插入图片描述
在这里插入图片描述

题意:

给定两个图, S S S T T T,’#'代表图案, ‘ . ’ ‘.’ .代表空,整幅图是 N ✖ N N✖N NN的大小,现在问你,能否旋转90°和平移的操作(操作数不限),来把S图中的图案,变成T中的图案

思路:

找到S图和T图中,图案的最左端、右端、上端、下端的坐标,上下左右坐标之外的地方说明都是空地,不用管,此时,此时,把新图(坐标是最左端右端上端下端之中的图),通过旋转,会有4种情况,旋转0,90,180,270,把旋转出来的图,存下来,看T图中是否有上述4种情况之一的图,可以小优化一下,如果找出来的图,一个是正方形另一个不是,那就直接不行。

#include <bits/stdc++.h>
using namespace std;
const int N = 210;
#define inf 0x3f3f3f3f
char S[N][N], T[N][N];
int n;
int u, l, r, d;
int U, L, R, D;
void change() {
    map<string, int> m;
    int f1, f2, f3, f4;
    f1 = f2 = f3 = f4 = 1;
    string s;
    for (int i = u; i <= d; i++) {
        for (int j = l; j <= r; j++) {
            s.push_back(S[i][j]);
        }
    }
    m[s]++;
    s.clear();
    for (int i = l; i <= r; i++) {
        for (int j = d; j >= u; j--) {
            s.push_back(S[j][i]);
        }
    }

    m[s]++;
    s.clear();
    for (int i = r; i >= l; i--) {
        for (int j = u; j <= d; j++) {
            s.push_back(S[j][i]);
        }
    }

    m[s]++;
    s.clear();
    for (int i = d; i >= u; i--) {
        for (int j = r; j >= l; j--) {
            s.push_back(S[i][j]);
        }
    }
    m[s]++;
    s.clear();
    for (int i = U; i <= D; i++) {
        for (int j = L; j <= R; j++) {
            s.push_back(T[i][j]);
        }
    }
    if (m[s] >= 1) {
        puts("Yes");
    } else
        puts("No");
    return;
}
int main() {
    cin >> n;
    u = U = l = L = inf;
    r = d = R = D = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> S[i][j];
            if (S[i][j] == '#') {
                u = min(u, i);
                d = max(d, i);
                l = min(l, j);
                r = max(r, j);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> T[i][j];
            if (T[i][j] == '#') {
                U = min(U, i);
                D = max(D, i);
                L = min(L, j);
                R = max(R, j);
            }
        }
    }
    //一方为正方形,另一方不是
    if (d - u == R - l && r - l != D - U && r - l == R - L && d - u != D - U) {
        puts("No");
        return 0;
    }
    change();

    return 0;
}

D - Rectangles

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

题意:

n n n个点的坐标,让你找四个坐标,组成一个矩形,问有多少个矩形的边与 x x x轴,或 y y y轴平行。

思路:

直接枚举矩形的右上端点( x 1 , y 1 x_1,y_1 x1,y1)和左下端点( x 2 , y 2 x_2,y_2 x2,y2),看是否右两个点的坐标( x 1 , y 2 x_1,y_2 x1,y2)( x 2 , y 1 x_2,y_1 x2,y1)来组成一个符合题意的矩形。
找( x 1 , y 2 x_1,y_2 x1,y2)( x 2 , y 1 x_2,y_1 x2,y1)这两个坐标的方法,可以用binary_search函数来二分找,也可以用桶提前把坐标存下来,来找。

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 2020;
#define x first
#define y second
int n;
int main() {
    scanf("%d", &n);
    vector<PII> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i].x >> a[i].y;
    }
    sort(a.begin(), a.end());
    int res = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (a[i].x < a[j].x && a[i].y < a[j].y) {
                if (binary_search(a.begin(), a.end(),
                                  make_pair(a[i].x, a[j].y)) &&
                    binary_search(a.begin(), a.end(),
                                  make_pair(a[j].x, a[i].y))) {
                    res++;
                }
            }
        }
    }
    printf("%d\n", res);
    return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct node {
    int x, y;
} e[N];
map<int, map<int, int>> vis;
int n;
bool cmp(node a, node b) {
    if (a.x != b.x) return a.x < b.x;
    return a.y < b.y;
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> e[i].x >> e[i].y;
        vis[e[i].x][e[i].y] = 1;
    }
    sort(e + 1, e + n + 1, cmp);
    int res = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (e[i].x < e[j].x && e[i].y < e[j].y) {
                if (vis[e[i].x][e[j].y] == 1 && vis[e[j].x][e[i].y] == 1) {
                    res++;
                }
            }
        }
    }
    cout << res << endl;
}

E - Destruction

在这里插入图片描述
在这里插入图片描述

题意:

给你一张连通图, n n n个点 m m m条边,现在让你在满足图是连通的情况下,删除一些边w,如果删除的边是正的,你将会得到+w的价值,如果删除的边是负的,你将会得到-w的价值,现在让你删除一些边,并保证图是连通的,问你能得到的最大价值。

思路:

所有边的权值和 − - 最小生成树(这个不是严格的最小生成树,所有负数边全放在生成树边中)的边权和= a n s ans ans

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = N * 2;
#define int long long
struct node {
    int a, b;
    int w;
    bool operator<(const node &t) const { return w < t.w; }
} e[M];
int fa[N];

int n, m;
int find(int x) {
    if (x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}

signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    int sum = 0;
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%lld%lld%lld", &a, &b, &c);
        e[i] = {a, b, c};
        sum += c;
    }
    sort(e, e + m);
    int res = 0;
    for (int i = 0; i < m; i++) {
        int a = e[i].a, b = e[i].b, c = e[i].w;
        a = find(a), b = find(b);
        if (a != b || (c < 0)) {
            fa[a] = b;
            res += c;
        }
    }
    cout << sum - res << endl;
}

F - Blocked Roads

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

给你一个 n n n个点 m m m条有向边的图,边权是1,让你现在让你枚举每条边,即第 i i i条边,输出删除第 i i i边的从 1 1 1 n n n的最短路,如果不可达,输出-1

思路:

先跑一边最短路,然后记录最短路的路径,如果删除的边不是最短路的路径,那么对最短路没有影响,否则就输出删除这条边后的最短路,时间复杂度O( N 2 l o g N N^2logN N2logN)

#include <bits/stdc++.h>
using namespace std;
const int N = 400, M = 2 * N * N;
#define inf 0x3f3f3f3f
typedef pair<int, int> PII;

int h[M], e[M], ne[M], idx;
bool vis[M];
int dis[M];
int n, m;
map<int, map<int, int> > st;
PII q[M];
void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
int pre[M];
void dijkstra(int x, int y) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    dis[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII> > q;
    q.push({0, 1});
    while (q.size()) {
        // cout<<dis[n]<<endl;
        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 (now == x && j == y) continue;
            if (dis[j] > distance + 1) {
                dis[j] = distance + 1;
                q.push({dis[j], j});
                pre[j] = now;
            }
        }
    }
}
int main() {
    memset(h, -1, sizeof(h));
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
        q[i] = {a, b};
    }
    dijkstra(-1, -1);
    int now = n;
    while (pre[now]) {
        st[pre[now]][now] = 1;
        now = pre[now];
    }
    int res;
    if (dis[n] >= inf) {
        res = -1;
    } else
        res = dis[n];
    for (int i = 0; i < m; i++) {
        if (st[q[i].first][q[i].second] == 1) {
            dijkstra(q[i].first, q[i].second);
            if (dis[n] >= inf)
                puts("-1");
            else
                cout << dis[n] << endl;
        } else
            cout << res << endl;
    }
    return 0;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值