Codeforces #363 div2 prob699

距离上次刷cf已经过去好久了吧,,

699A:Launch of Collider:

大概题意:

有一个有n个点的序列,序列中每一个点都有一个一维坐标以及移动方向,数据保证坐标是偶数,保证坐标升序给出,要求存在两个点相碰的最短时间

题解:

显然可以发现,相碰的两个点只可能是开始时相邻的两个点,而且一定保证左边的点的方向为”R”右边的点为”L”,那么直接扫一遍,对于可行的点对,用 (f[i]f[i+1])/2 更新答案

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 200000 + 500;
const int inf = 0x7fffffff;
char s[maxn];
int x[maxn];
int n;
int ans = inf;

int main () {
    scanf("%d", &n);
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i++) scanf("%d", &x[i]);
    for (int i = 1; i <= n; i++) {
        if (s[i] == 'R' && s[i+1] == 'L') {
            ans = std :: min(ans, (x[i+1] - x[i]) / 2);
        }
    }
    if (ans == inf) printf("-1");
    else printf("%d", ans);
    return 0;
}

699B:One Bomb:

大概题意:

对于一个n*m的地面,有若干个墙,其余点为空地,题目中给出一个炸弹,可以炸掉炸弹所在行和列的墙,炸弹可以放在地面上的任何位置,问能否用这一个炸弹使地面上没有墙,输出一个可行的位置,如果不能,输出”NO”

题解:

用fx[i]表示第i行的墙数,用fy[j]表示每列的墙数,记录总炸弹数为sumb,枚举炸弹的位置(i, j),每次check,如果 fx[x]+fy[y](mapp[x][y]==1)==sumb 即成立

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 1200;
int mapp[maxn][maxn];
char c;
int n, m;
int sumb;
int fx[maxn], fy[maxn];


bool check(int x, int y) {
    int cur = fx[x] + fy[y];
    if (mapp[x][y] == 1) cur--;
    if (cur == sumb) return 1;
    return 0;
}

int main () {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        getchar();
        for (int j = 1; j <= m; j++) {
            c = getchar();
            if (c == '*') {
                sumb++;
                fx[i]++;
                fy[j]++;
                mapp[i][j] = 1;
            }
        }
    }
    // for (int i = 1; i <= n; i++) {
    //     for (int j = 1; j <= m; j++) printf("%d", mapp[i][j]);
    //     printf("\n");
    // }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (check(i, j)){
                printf("YES\n%d %d", i, j);
                exit(0);
            }
        }
    printf("NO");
    return 0;
}

699C Vacations:

为什么感觉C题比B题还水呢,,,

大概题意:

题中人有运动和在线比赛两种选择,而对于某一天,有四种可能:

  • 不能操作
  • 可以运动
  • 可以比赛
  • 可以运动和比赛
    题中人不能在相邻的两天进行相同操作(即,如果第3天进行了运动,第4天就不能进行运动)

题解:

显然是个dp题目咯=。=
设dp[i][j]表示在第i天,当前天选择了j操作时的最小值
j = 0表示休息,j = 1表示运动 j = 2,表示比赛

dp[i][0]=min(dp[i][0]dp[i1][j]+1)j=012

dp[i][1]=min(dp[i][1]dp[i1][j])j=02

dp[i][2]=min(dp[i][2]dp[i1][j])j=01

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 150;
int dp[maxn][3];    //  0 rest  1 sport 2 contest
int n; 
int a[maxn];

int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    memset(dp, 127, sizeof(dp));
    dp[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] == 0) 
            for (int j = 0; j <= 2; j++) 
                dp[i][0] = std :: min(dp[i-1][j] + 1, dp[i][0]);
        if (a[i] == 1) {
            for (int j = 0; j <= 2; j++) 
                dp[i][0] = std :: min(dp[i-1][j] + 1, dp[i][0]);
            dp[i][2] = std :: min(dp[i-1][0], dp[i][2]);
            dp[i][2] = std :: min(dp[i-1][1], dp[i][2]);
        }
        if (a[i] == 2) {
            for (int j = 0; j <= 2; j++) 
                dp[i][0] = std :: min(dp[i-1][j] + 1, dp[i][0]);
            dp[i][1] = std :: min(dp[i-1][0], dp[i][1]);
            dp[i][1] = std :: min(dp[i-1][2], dp[i][1]);
        }
        if (a[i] == 3) {
            for (int j = 0; j <= 2; j++) 
                dp[i][0] = std :: min(dp[i-1][j] + 1, dp[i][0]);
            dp[i][1] = std :: min(dp[i-1][0], dp[i][1]);
            dp[i][1] = std :: min(dp[i-1][2], dp[i][1]);
            dp[i][2] = std :: min(dp[i-1][0], dp[i][2]);
            dp[i][2] = std :: min(dp[i-1][1], dp[i][2]);
        }
    }
    int ans = 0x7fffffff;
    for (int j = 0; j <= 2; j++) ans = std :: min(ans, dp[n][j]);
    printf("%d", ans);

    return 0;
} 

699D :Fix a Tree

简要题意:

题目中给定一个n和数组father,其中father[i]表示i的父亲节点,题目中定义可构成一棵树的father数组为可行数组,要求修改father数组,使之成为一个可行数组,输出最小的修改次数以及任意一个修改方案

题解:

  • 不难发现这张图将会由多个章鱼图(即n个点n条边的基环树)和树构成
  • 既然题目要求使最后形成一棵树,那么一定同时满足联通和无环
  • 首先考虑无环,对于一个章鱼图,可以随意拆掉环上的任何一个点,并把它的father值修改,对于一棵树则无需修改
  • 考虑联通,如果想让两棵树联通,那么只需将其中一棵的根节点的father置为另一棵,如果有环的话,将环上任意一点的father值设为另一棵的根节点,如果是两个基环树,则需要将其中一个的father置为本身,另一个置为该值
  • 综上,先对图进行tarjan,如果结果中没有树,则ans=环的数量,否则ans=环的数量+树的数量-1
  • 对于方案,如果没有树,则从任意环中取出一个节点,以之为根,从其他所有环中各取一点与其相连。如果有树,则以某一棵树的根为总根,另其他树和环都与之相连,此时的father数组即为答案

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 200000 + 5000;
int n;
int a[maxn];
int last[maxn], pre[maxn], other[maxn];
int tot = 1;
int dfn[maxn], low[maxn], sta[maxn];
bool vis[maxn];
int statot = 0;
int cur = 0;
int belong[maxn];
int point[maxn];        //  if k is a cycle then make point[k] connect to the root
int tim = 0;
int size[maxn];

void tarjan(int x) {
    tim++;
    dfn[x] = low[x] = tim;
    statot++;
    sta[statot] = x;
    vis[x] = 1;
    for (int p = last[x]; p; p = pre[p]) {
        int q = other[p];
        if (!dfn[q]) {
            tarjan(q);
            low[x] = std :: min(low[x], low[q]);
        } else if (vis[q]) {
            low[x] = std :: min(low[x], dfn[q]);
        }
    }
    if (dfn[x] == low[x]) {
        cur++;
        while (sta[statot] != x) {
            vis[sta[statot]] = 0;
            size[cur]++;
            belong[sta[statot]] = cur;
            statot--;
        }
        statot--;
        belong[x] = cur;
        vis[x] = 0;
        size[cur]++;
        point[cur] = x;
    }
}
void add(int x, int y) {
    tot++;
    pre[tot] = last[x];
    last[x] = tot;
    other[tot] = y;
}
int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        add(a[i], i);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan(i);
    }
    int ans = 0;
    int ansk = 0;
    for (int i = 1; i <= cur; i++) {
        if (size[i] == 1) {
            if (a[point[i]] == point[i]) {
                ans++;
                ansk = point[i];
            } 
        } else {
            ans++;
        }
    }
    if (ansk > 0) ans--;
    printf("%d\n", ans);
    if (ansk == 0) {
        for (int i = 1; i <= cur; i++)
            if (size[i] > 1) {
                ansk = point[i];
                break;
            }
    }
    for (int i = 1; i <= cur; i++) {
        if (size[i] == 1 && a[point[i]] != point[i]) continue;
        a[point[i]] = ansk;
    }
    for (int i = 1; i <= n; i++) printf("%d ", a[i]);
    return 0;
}
发布了90 篇原创文章 · 获赞 65 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ctsnevermore/article/details/53155443