AtCoder Beginner Contest 225(补题)

D - Play Train

链接: link.

题意:

N N N个或者分离的火车头,现在有 Q Q Q次操作,每次操作有 3 3 3种类型, 1. x 1.x 1.x y y y代表把 x x x车的尾部和 y y y车的头部连接起来
2. x 2.x 2.x y y y代表把 x x x车的尾部和 y y y车的头部分离开
3. x 3.x 3.x代表 输出与 x x x车连接的所有车,输出个数并从头到尾输出各个车的编号

思路:

用非路径压缩版并查集就可以解决这个问题,直接按照输入的操作,分别实现并查集的合并、分离,并按照顺序输出并查集元素。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 10;
int fa[N];
int son[N];
int n, q;
int find(int x) {
    if (x != fa[x]) return find(fa[x]);
    return x;
}

void Union(int a, int b) {
    fa[b] = a;
    son[a] = b;
}

void del(int a, int b) {
    fa[b] = b;
    son[a] = a;
}

void print(int x) {
    int st = find(x);
    vector<int> res;
    while (son[st] != st) {
        res.push_back(st);
        st = son[st];
    }
    res.push_back(st);
    cout << res.size() << " ";
    for (int i = 0; i < res.size(); i++) {
        cout << res[i] << " ";
    }
    puts("");
}
int main() {
    cin >> n >> q;
    for (int i = 0; i < N; i++) {
        fa[i] = i;
        son[i] = i;
    }
    while (q--) {
        int op, x, y;
        scanf("%d%d", &op, &x);
        if (op == 1) {
            scanf("%d", &y);
            Union(x, y);
        }
        if (op == 2) {
            scanf("%d", &y);
            del(x, y);
        }
        if (op == 3) {
            print(x);
        }
    }
}

E - 7

链接: link.

题意:

在二维坐标轴中,给 N N N个坐标,在二维坐标系中有 N N N 7 7 7, 7 7 7是由坐标 ( x i , y i ) (x_i,y_i) (xi,yi) ( x i − 1 , y i ) (x_{i-1},y_i) (xi1,yi)构成的线段和 ( x i , y i ) (x_i,y_i) (xi,yi) ( x i , y i − 1 ) (x_{i},y_{i-1}) (xi,yi1)g构成的线段组成,如果一个 7 7 7与坐标原点形成的四边形,与其他的四边形没有发生冲突(即没有交点),就说明 7 7 7是好的,现在问有多少个好 7 7 7

思路:

对于一个 ( x i , y i ) (x_i,y_i) (xi,yi)考虑与 ( x i − 1 , y i ) (x_{i-1},y_i) (xi1,yi) ( x i , y i − 1 ) (x_i,y_{i-1}) (xi,yi1)构成的图形,就是以 ( x i − 1 , y i ) (x_{i-1},y_i) (xi1,yi) ( x i , y i − 1 ) (x_i,y_{i-1}) (xi,yi1)和原点所形成的两条直线所占据的图形,这两条直线的斜率为 k 1 和 k 2 k1和k2 k1k2。如果某条直线与当前这个图形的两条直线有交点,那就说明这条直线的斜率在 [ k 1 , k 2 ] [k1,k2] [k1,k2]之间。所以说每个 7 7 7所占有的斜率 [ k 1 , k 2 ] [k1,k2] [k1,k2],可以看作在数轴上占据了这段这段区间,现在问题转换为最大不相交区间个数(不包括端点)
也可以通过极角排序来实现,一样的原理。
注:因精度问题不开long double 过不了

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;

int main() {
    int n;
    cin >> n;
    vector<pair<long double, long double> > s(n);
    for (int i = 0; i < n; i++) {
        ld x, y;
        cin >> x >> y;
        s[i].second = atan2(y - 1, x);
        s[i].first = atan2(y, x - 1);
    }

    sort(s.begin(), s.end());

    ld now = 0.0;
    ll res = 0;

    for (int i = 0; i < n; i++) {
        if (now <= s[i].second) {
            res++;
            now = s[i].first;
        }
    }
    cout << res << endl;
}

F - String Cards

链接: link.

题意:

给定 N N N个字符串,现在让你选出 K K K个字符串,可以以任意顺序拼接,现在让你输出拼接后字典序最小的字符串。

思路:

如果此题没有选 K K K个字符串的限制,而是任意拼接然后输出字典序最小,题目就会变成下面这道题
题目链接: link.
链接: link
在没有限制的情况下,就只需要对字符串拼接后的大小来进行排序。即

bool cmp(string a, string b) { return a + b < b + a; }

含义就是排序的后面的子串不管是什么样只要往前连,肯定使连起来的字符串变小,说白了 就是两个字符串 a b ab ab b a ba ba排序,要是 a b ab ab b a ba ba a a a就放 b b b左边。此时排完序后,定义一个 d p dp dp方程
d p ( i , j ) dp(i,j) dp(i,j),代表从字符串 s i , s i + 1 . . . . . . s n s_i,s_{i+1}......s_{n} si,si+1......sn中选 j j j个字符串拼接到当前字符串上形成的最小字典序字符串,那么此时
d p ( i , j ) = m i n ( d p ( i + 1 , j ) , s i + d p ( i + 1 , j − 1 ) ) dp(i,j)=min(dp(i+1,j),s_i+dp(i+1,j-1)) dp(i,j)=min(dp(i+1,j),si+dp(i+1,j1))
因为如果我选了 s i s_i si,那么 d p ( i , j ) = s i + d p ( i + 1 , j − 1 ) dp(i,j)=s_i+dp(i+1,j-1) dp(i,j)=si+dp(i+1,j1)
如果我不选 s i s_i si,那么此时 d p ( i , j ) = ( d p ( i + 1 , j ) dp(i,j)=(dp(i+1,j) dp(i,j)=(dp(i+1,j)
所以最后输出 d p ( 1 , K ) dp(1,K) dp(1,K)
动态规划+字符串排序

#include <bits/stdc++.h>
using namespace std;
const int N = 111;
int n, k;
string s[N];
string dp[N][N];

bool cmp(string a, string b) { return a + b < b + a; }

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> s[i];
    }
    for (int i = 1; i <= n + 1; i++) {
        for (int j = 0; j <= k; j++) {
            dp[i][j].push_back('z' + 1);
        }
    }
    sort(s + 1, s + n + 1, cmp);
    dp[n + 1][0] = "";
    for (int i = n; i >= 1; i--) {
        dp[i][0] = "";
        for (int j = 1; j <= k; j++) {
            dp[i][j] = min(dp[i + 1][j], s[i] + dp[i + 1][j - 1]);
        }
    }

    cout << dp[1][k] << endl;
}

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值