Codeforces Round #605 (Div. 3)

https://codeforces.com/contest/1272/

A - Three Friends

题意:给三个点,每个点至多移动一格,求三个点两两之间的距离之和的最小值。

题解:排序之后中间那个点动不动无所谓,左右的点向中间靠近可以减少线段的程度至多2,长度最短减少到0。显然答案就是线段长度的两倍。

void test_case() {
    int a[4];
    scanf("%d%d%d", &a[1], &a[2], &a[3]);
    sort(a + 1, a + 1 + 3);
    int maxd = a[3] - a[1];
    if(maxd <= 2)
        maxd = 0;
    else {
        maxd -= 2;
        maxd *= 2;
    }
    printf("%d\n", maxd);
}

B - Snow Walking Robot

题意:给一个图,从原点开始上下左右走,走的轨迹除了原点之外不能有任何一个交点,且原点只能经过两次。给一个序列,你可以重排其中的走法,假如不行则先删除尽可能少的再重排。

题解:假如有上下左右至少各一个,那么走一个矩形。否则若只有上下或者只有左右那么就走一步就回来。

注意字符串的开始是从1开始,还调半天,真是演。

char s[100005];
void test_case() {
    scanf("%s", s + 1);
    int cntu = 0, cntl = 0, cntd = 0, cntr = 0;
    int n = strlen(s + 1);
    for(int i = 1; i <= n; ++i) {
        if(s[i] == 'U')
            ++cntu;
        else if(s[i] == 'D')
            ++cntd;
        else if(s[i] == 'L')
            ++cntl;
        else
            ++cntr;
    }
    cntl = min(cntl, cntr);
    cntr = cntl;
    cntu = min(cntu, cntd);
    cntd = cntu;
    if(cntr == 0) {
        if(cntd) {
            puts("2");
            puts("UD");
            return;
        } else {
            puts("0");
            puts("");
            return;
        }
    } else if(cntd == 0) {
        puts("2");
        puts("LR");
        return;
    }
 
    int n2 = cntl + cntr + cntu + cntd;
    printf("%d\n", n2);
    while(cntu--)
        putchar('U');
    while(cntr--)
        putchar('R');
    while(cntd--)
        putchar('D');
    while(cntl--)
        putchar('L');
    putchar('\n');
    return;
}

C - Yet Another Broken Keyboard

题意:给一个串,和一堆字符,求只由这些字符构成的子串的数量。

题解:随便组合一下。

char s[200005];
bool vis[128];
void test_case() {
    int n, k;
    scanf("%d%d%s", &n, &k, s + 1);
    for(int i = 1; i <= k; ++i) {
        char c[2];
        scanf("%s", c);
        vis[c[0]] = 1;
    }
    ll ans = 0, tmp = 0;
    for(int i = 1; i <= n; ++i) {
        if(vis[s[i]]) 
            ++tmp;
        else {
            ans += tmp * (tmp + 1) / 2;
            tmp = 0;
        }
    }
    ans += tmp * (tmp + 1) / 2;
    tmp = 0;
    printf("%lld\n", ans);
}

D - Remove One Element

题意:从一个序列中删除至多一个元素,求生成的新序列中的最长严格上升子串的长度。

题解:比较恶心的一个题,一开始考虑dp,但其实有更简单的做法:划分出每段严格上升的段,然后只有当段的开头或者段的结尾被删除时有可能会连接前后两个不同的段。注意假如一个段只有一个元素时的情况。

int a[200005], fi[200005], se[200005];
void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int top = 1;
    fi[1] = 1, se[1] = 1;
    for(int i = 1; i <= n; ++i) {
        if(a[i] > a[i - 1])
            se[top] = i;
        else {
            fi[++top] = i;
            se[top] = i;
        }
    }
    int ans = se[1] - fi[1] + 1;
    for(int i = 2; i <= top; ++i) {
        ans = max(ans, se[i] - fi[i] + 1);
        if(se[i] == fi[i] && i + 1 <= top) {
            if(a[fi[i + 1]] > a[se[i - 1]])
                ans = max(ans, se[i - 1] - fi[i - 1] + 1 + se[i + 1] - fi[i + 1] + 1);
        } else {
            if(a[se[i - 1] - 1] < a[fi[i]])
                ans = max(ans, se[i - 1] - fi[i - 1] + 1 + se[i] - fi[i] + 1 - 1);
            else if(a[se[i - 1]] < a[fi[i] + 1])
                ans = max(ans, se[i - 1] - fi[i - 1] + 1 + se[i] - fi[i] + 1 - 1);
        }
    }
    printf("%d\n", ans);
}

假如是dp的话,需要考虑的是前面有没有元素被删除过。但是同时也受末尾的状态影响。设dp[0][i]表示不进行删除时的以i为结尾的最长连续上升子串的长度,dp[1][i]为删除了i的,dp[2][i]为已经使用了删除机会,且删除的不是i的。

int a[200005], dp[3][200005];
void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int ans = 0;
    for(int i = 1; i <= n; ++i) {
        dp[0][i] = a[i] > a[i - 1] ? dp[0][i - 1] + 1 : 1;
        dp[1][i] = dp[0][i - 1];
        dp[2][i] = max(a[i] > a[i - 1] ? dp[2][i - 1] + 1 : 1, i >= 2 ? (a[i] > a[i - 2] ? dp[1][i - 1] + 1 : 1) : 1);
        ans = max(ans, dp[0][i]);
        ans = max(ans, dp[2][i]);
    }
    printf("%d\n", ans);
}

猜你喜欢

转载自www.cnblogs.com/KisekiPurin2019/p/12036076.html