2018年全国多校算法寒假训练营练习比赛(第四场)题解

题目链接

A - 石油采集

题意:有一个$01$矩阵,每次可以拿走两个相邻的$1$,问最多能操作几次。

这题和HDU 1507一样。二维矩阵四连通图是一个二分图,题目的操作事实上就是求这个二分图的最大匹配。

B - 道路建设

最小生成树

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 10;
int f[maxn];

struct Edge {
  int u, v, cost;
}s[maxn];

bool cmp(Edge &a, Edge &b) {
  return a.cost < b.cost;
}

int Find(int x ){
  if(x != f[x]) f[x] = Find(f[x]);
  return f[x];
}

int main() {
  int c, n, m;
  while(~scanf("%d%d%d", &c, &n, &m)) {
    for(int i = 1; i <= m; i ++) {
      f[i] = i;
    }
    for(int i = 1; i <= n; i ++) {
      scanf("%d%d%d", &s[i].u, &s[i].v, &s[i].cost);
    }
    sort(s + 1, s+1+n, cmp);
    for(int i =1; i <= n; i ++) {
      int fu = Find(s[i].u);
      int fv = Find(s[i].v);
      if(fu == fv) continue;
      f[fu] = fv;
      c -= s[i].cost;
      if(c < 0) break;
    }
    if(c < 0) printf("No\n");
    else printf("Yes\n");
  }
  return 0;
}

C - 求交集

类似于归并排序那样搞就可以了。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10;
int a[maxn], b[maxn], ans[maxn];
int n, m, sz;

int main() {
  while(~scanf("%d%d", &n, &m)) {
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    for(int i = 1; i <= m; i ++) scanf("%d", &b[i]);
    int p1 = 1, p2 = 1;
    sz = 0;
    while(p1 <= n && p2 <= m) {
      if(a[p1] == b[p2]) {
        ans[sz ++] = a[p1];
        p1 ++;
        p2 ++;
      } else {
        if(a[p1] > b[p2]) p2 ++;
        else p1 ++;
      }
    }
    if(sz == 0) {
      printf("empty\n");
      continue;
    }
    for(int i = 0; i < sz; i ++) {
      printf("%d", ans[i]);
      if(i < sz -  1) printf(" ");
      else printf("\n");
    }
  }
  return 0;
}

D - 小明的挖矿之旅

由于只能向右或者向下走,所以变成了一张有向无环图。我们只要比较出度为$0$的点的个数和入度为$0$的点的个数即可。

但要注意几种特殊情况:全是孤立点、没有点。

#include <bits/stdc++.h>
using namespace std;

const int INF = 0x7FFFFFFF;
const int maxn = 1100;
char s[maxn][maxn];
int in[maxn * maxn];
int ou[maxn * maxn];
int n, m;

int out(int x, int y) {
  if(x < 0 || x >= n) return 1;
  if(y < 0 || y >= m) return 1;
  return 0;
}

int main() {
  while(~scanf("%d%d", &n, &m)) {
    for(int i = 0; i < n; i ++) {
      scanf("%s", s[i]);
    }
    memset(in, 0, sizeof in);
    memset(ou, 0, sizeof ou);
    for(int i = 0; i < n; i ++) {
      for(int j = 0; j < m; j ++) {
        if(s[i][j] == ‘#‘) continue;
        if(out(i, j + 1) == 0 && s[i][j + 1] != ‘#‘) {
          in[i * m + j + 1] ++;
          ou[i * m + j] ++;
        }
        if(out(i + 1, j) == 0 && s[i + 1][j] != ‘#‘) {
          in[(i + 1) * m + j] ++;
          ou[i * m + j] ++;
        }
      }
    }

    int sum1 = 0, sum2 = 0, sum3 = 0;
    for(int i = 0; i < n; i ++) {
      for(int j = 0; j < m; j ++) {
        if(s[i][j] == ‘#‘) continue;
        if(in[i * m + j] == 0) sum1 ++;
        if(ou[i * m + j] == 0) sum2 ++;
        sum3 ++;
      }
    }

    if(sum3 == 0) {
      printf("%d\n", 0);
      return 0;
    }
    if(sum1 == sum3 && sum2 == sum3) {
      printf("%d\n", sum1 - 1);
    }
    else
    printf("%d\n", max(sum1, sum2));
  }

  return 0;
}

E - 通知小弟

先对图进行强连通分量缩点,因为一个强连通分量内部一旦有一个人得到通知,所有人都可以得到通知。

缩点后形成了一张有向无环图,我们只要通知到新图中那些入度为$0$的点,所有的人都能得到通知。

对于无解的情况,我们只要检查HA能通知到的人是否cover了新图中所有入度为$0$的点。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 550;
int n, m;
int a[maxn];
int ans[maxn];
int in[maxn];

#define MAXN 550
struct Node {
  int v;
  int next;
} edge[500010];
int sz;
int head[MAXN];
int dfn[MAXN];
int low[MAXN];
bool mark[MAXN]; //to judge in stack or not
int id[MAXN]; //the id of scc
int T;
stack<int> sta;
int scc;
void tarjan(int v) {
  dfn[v] = low[v] = ++T;
  sta.push(v);
  mark[v] = true;
  int k;
  for (k = head[v]; k != -1; k = edge[k].next) {
    int u = edge[k].v;
    if (!dfn[u]) {
      tarjan(u);
      low[v] = min(low[v], low[u]);
    } else if (mark[u])
      low[v] = min(low[v], dfn[u]);
  }
  if (low[v] == dfn[v]) {
    ++scc;
    int u;
    do {
      u = sta.top();
      sta.pop();
      mark[u] = false;
      id[u] = scc;
    } while (u != v);
  }
  return;
}
void solve(int n) {
  scc = 0;
  int i;
  for (i = 1; i <= n; ++i)
    if (!dfn[i])
      tarjan(i);
  memset(mark, false, sizeof (mark));
  int k;
  for (i = 1; i <= n; ++i)
    for (k = head[i]; k != -1; k = edge[k].next)
      if (id[i] != id[edge[k].v])
        mark[id[i]] = true;

  return ;
  for(int i = 1; i <= n; i ++) {
    printf("%d : %d\n", i, id[i]);
  }

  int p;
  int cnt(0);
  for (i = 1; i <= scc; ++i) {
    if (!mark[i]) {
      ++cnt;
      p = i;
    }
  }
  if (cnt > 1)
    printf("0\n");
  else {
    cnt = 0;
    for (i = 1; i <= n; ++i)
      if (id[i] == p) ++cnt;
    printf("%d\n", cnt);
  }
}

int main() {
  while(~scanf("%d%d", &n, &m)) {
    for(int i = 1; i <= m; i ++) {
      scanf("%d", &a[i]);
    }

    for (int i = 1; i <= n; ++i) {
      head[i] = -1;
      dfn[i] = 0;
      mark[i] = false;
    }

    sz = 0;
    for (int i = 1; i <= n; ++i) {
      int num;
      scanf("%d", &num);
      while(num --) {
        int to;
        scanf("%d", &to);
        edge[sz].v = to;
        edge[sz].next = head[i];
        head[i] = sz ++;
      }
    }

    solve(n);

    memset(in, 0, sizeof in);
    memset(ans, 0, sizeof ans);

    for(int i = 1; i <= n; i ++) {
      for(int j = head[i]; j != -1; j = edge[j].next) {
        if(id[i] != id[edge[j].v])
        in[id[edge[j].v]] ++;
      }
    }

    int pp = 0;
    for(int i = 1; i <= scc; i ++) {
      if(in[i] == 0) pp ++;
    }
    for(int i = 1; i <= m; i ++) {
      if(in[id[a[i]]] == 0) {
        ans[id[a[i]]] = 1;
      }
    }

    int out = 0;
    for(int i = 1; i <= scc; i ++) {
      out += ans[i];
    }
    if(out == pp) {}
    else out = -1;
    cout << out << endl;
  }
  return 0;
}

F - Call to your teacher

从$1$号点开始$dfs$,如果$n$能被遍历到就是可以的。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 500;
int n, m;
vector<int> g[maxn];
int f[maxn];

void dfs(int x) {
  f[x] = 1;
  for(int i = 0; i < g[x].size(); i ++) {
    if(f[g[x][i]]) continue;
    dfs(g[x][i]);
  }
}

int main() {
  while(~scanf("%d%d", &n, &m)) {
    for(int i = 1; i <= n; i ++) {
      g[i].clear();
      f[i] = 0;
    }
    while(m --) {
      int x, y;
      scanf("%d%d", &x, &y);
      g[x].push_back(y);
    }
    dfs(1);
    if(f[n]) printf("Yes\n");
    else printf("No\n");
  }
  return 0;
}

G - 老子的意大利炮呢

可以枚举三种配件按什么顺序获得,得到之后再走到终点即可。最后阶段可以bfs。

#include <bits/stdc++.h>
using namespace std;

const int INF = 0x7FFFFFFF;
const int maxn = 110;
char s[maxn][maxn];
int dis[maxn][maxn];
int ans;
int n, m;
int sx, sy;
int x[5], y[5];
int ex, ey;
int t[5];

int dir[4][2] = {
  {-1, 0},
  {1, 0},
  {0, -1},
  {0, 1},
};

int out(int x, int y) {
  if(x < 0 || x >= n) return 1;
  if(y < 0 || y >= m) return 1;
  return 0;
}

void bfs() {
  queue<int> q;
  q.push(ex * m + ey);
  dis[ex][ey] = 0;
  while(!q.empty()) {
    int top = q.front();
    q.pop();
    int nowx = top / m;
    int nowy = top % m;
    for(int i = 0; i < 4; i ++) {
      int tx = nowx + dir[i][0];
      int ty = nowy + dir[i][1];
      if(out(tx, ty)) continue;
      if(s[tx][ty] == ‘#‘) continue;
      if(dis[nowx][nowy] + t[0] + t[1] + t[2] + 1 > dis[tx][ty]) continue;
      dis[tx][ty] = dis[nowx][nowy] + t[0] + t[1] + t[2] + 1;
      q.push(tx * m + ty);
    }
  }
}

int D(int x1, int y1, int x2, int y2) {
  return abs(x1 - x2) + abs(y1 - y2);
}

int main() {
  scanf("%d%d", &n, &m);
  for(int i = 0; i < n; i ++) {
    scanf("%s", s[i]);
    for(int j = 0; j < m; j ++) {
      dis[i][j] = INF;
    }
  }
  scanf("%d%d", &sx, &sy);
  sx --; sy --;
  for(int i = 0; i < 3; i ++) {
    scanf("%d%d", &x[i], &y[i]);
    x[i] --;
    y[i] --;
  }
  scanf("%d%d", &ex, &ey);
  ex --; ey --;
  for(int i = 0; i < 3; i ++) {
    scanf("%d", &t[i]);
  }
  bfs();

  /*
  for(int i = 0; i< n; i ++) {
    for(int j = 0; j < m; j ++) {
      printf("%d ", dis[i][j]);
    }
    printf("\n");
  }
   */
  ans = INF;
  for(int i = 0; i < 3; i ++) {
    if(dis[x[i]][y[i]] == INF) continue;
    int tmp = 0;
    int p0, p1, p2;
    if(i == 0) {
      p0 = 0;
      p1 = 1;
      p2 = 2;
    } else if(i == 1) {
      p0 = 1;
      p1 = 0;
      p2 = 2;
    } else {
      p0 = 2;
      p1 = 1;
      p2 = 0;
    }

    tmp = min(D(sx, sy, x[p1], y[p1]) * 1
              + D(x[p1], y[p1], x[p2], y[p2]) * (t[p1] + 1)
              + D(x[p2], y[p2], x[p0], y[p0]) * (t[p1] + t[p2] + 1),
              D(sx, sy, x[p2], y[p2]) * 1
              + D(x[p1], y[p1], x[p2], y[p2]) * (t[p2] + 1)
              + D(x[p1], y[p1], x[p0], y[p0]) * (t[p1] + t[p2] + 1)
              );

    tmp += dis[x[i]][y[i]];
    ans = min(ans, tmp);
  }
  cout << ans << endl;
  return 0;
}

H - 老子的全排列呢

可以手写$dfs$生成,也可以调用函数。

#include <bits/stdc++.h>
using namespace std;

int a[10];

int main() {
  for(int i = 1; i <= 8; i ++) {
    a[i] = i;
  }
  do {
    for(int i = 1; i <= 8; i ++) {
      cout << a[i];
      if(i < 8) cout << " ";
    }
    cout << endl;
  } while(next_permutation(a + 1, a + 9));
  return 0;
}

原文地址:https://www.cnblogs.com/zufezzt/p/8442724.html

时间: 2024-10-09 17:38:29

2018年全国多校算法寒假训练营练习比赛(第四场)题解的相关文章

【2018年全国多校算法寒假训练营练习比赛(第四场)-A】石油采集(匈牙利算法)

试题链接:https://www.nowcoder.com/acm/contest/76/A [思路] 每个‘#’的右边和下边如果也是‘#’说明这两个点构成通路,以此重构一幅图,然后找二分图的最大匹配. [代码] #include<bits/stdc++.h> using namespace std; char mp[55][55]; bool vis[2505]; vector<int>G[2505]; int mp1[55][55], match[2505], n; bool

【2018年全国多校算法寒假训练营练习比赛(第四场)-D】小明的挖矿之旅

题目链接:https://www.nowcoder.com/acm/contest/76/D 做题时没注意到“无论出现在哪个格子”..题中也没说明一个格子只能经过一次,其实没有想象的复杂. 判断如果点的下边或右边不能走,传送门数+1.特判只有一个‘.’传送门数为0 代码: #include<bits/stdc++.h> using namespace std; char mp[1004][1004]; int main() { int n, m, i, j; while(~scanf(&quo

【题集】牛客网&#183;2018年全国多校算法寒假训练营练习比赛(第二场)

原文链接:2018年全国多校算法寒假训练营练习比赛(第二场) A 吐泡泡 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言65536K64bit IO Format: %lld 题目描述 小鱼儿吐泡泡,嘟嘟嘟冒出来.小鱼儿会吐出两种泡泡:大泡泡"O",小泡泡"o". 两个相邻的小泡泡会融成一个大泡泡,两个相邻的大泡泡会爆掉. (是的你没看错,小气泡和大气泡不会产生任何变化的,原因我也不知道.) 例如:ooOOoooO经过一段时间

2018年全国多校算法寒假训练营练习比赛(第一场)G.圆圈

链接:https://www.nowcoder.com/acm/contest/67/G来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言65536K 64bit IO Format: %lld 题目描述 圈圈圆圆圈圈,lulu小朋友最近看喜羊羊看多了,老是受刺激就画圆圈,听到小于8的数字时,还会画出十分有规律的圆圈,现在你需要根据样例观察出规律,编写程序,根据输入的数字n(n<8),输出对应的圆圈. 输入描述: 第一行是样例数T(T<9)第2

2018年全国多校算法寒假训练营练习比赛(第一场)D N阶汉诺塔变形

https://www.nowcoder.com/acm/contest/67/D 思路: 先手动模拟一下过程,以下是模拟过程,按顺序表示第几步需要移动的盘标号 1 1 2 1 1 2 1 1 3 1 1 2 1 1 2 1 1 3 1 1 2 1 1 2 1 1 4 1 1 2 ...... 我们发现每出现两次1就会出现一次2,每两次2就会出现一次3,每两次3就会出现一次4,每两次4就会出现一次5...... 然后我们发现如果把所有大于1的标号看成1,那么k步1出现的次数是 k/1 如果把所有

2018年全国多校算法寒假训练营练习比赛(第二场)

A题: 链接:https://www.nowcoder.com/acm/contest/74/A来源:牛客网 小鱼儿吐泡泡,嘟嘟嘟冒出来.小鱼儿会吐出两种泡泡:大泡泡"O",小泡泡"o". 两个相邻的小泡泡会融成一个大泡泡,两个相邻的大泡泡会爆掉. (是的你没看错,小气泡和大气泡不会产生任何变化的,原因我也不知道.) 例如:ooOOoooO经过一段时间以后会变成oO. 输入描述: 数据有多组,处理到文件结束.每组输入包含一行仅有'O'与'o'组成的字符串. 输出描述

2018年全国多校算法寒假训练营练习比赛(第二场)F - 德玛西亚万岁

链接:https://www.nowcoder.com/acm/contest/74/F来源:牛客网 题目描述 德玛西亚是一个实力雄厚.奉公守法的国家,有着功勋卓著的光荣军史. 这里非常重视正义.荣耀.职责的意识形态,这里的人民为此感到强烈自豪. 有一天他们想去制裁邪恶的比尔吉沃特,于是派遣了自己最优秀的战士. 结果比尔吉沃特领土太小,只有长为n宽为m共计n*m块土地,其中有些土 地标记为0表示为高山峻岭或者深海湖泊,英雄们无法在其中站立,只有标 记为1的土地才能容纳一个英雄.德玛西亚的英雄们战

2018年全国多校算法寒假训练营练习比赛(第二场)A.吐泡泡

原题链接 描述 小鱼儿吐泡泡,嘟嘟嘟冒出来.小鱼儿会吐出两种泡泡:大泡泡"O",小泡泡"o". 两个相邻的小泡泡会融成一个大泡泡,两个相邻的大泡泡会爆掉. (是的你没看错,小气泡和大气泡不会产生任何变化的,原因我也不知道.) 注意:自左到右进行合并. 例如:ooOOoooO经过一段时间以后会变成oO. 输入 数据有多组,处理到文件结束. 每组输入包含一行仅有'O'与'o'组成的字符串. 输出 每组输出仅包含一行,输出一行字符串代表小鱼儿吐出的泡泡经过融合以后所剩余的

【2018年全国多校算法寒假训练营练习比赛(第五场)-E】情人节的电灯泡(二维树状数组单点更新+区间查询)

试题链接:https://www.nowcoder.com/acm/contest/77/E 题目描述 情人节到了,小芳和小明手牵手,打算过一个完美的情人节,但是小刚偏偏也来了,当了一个明晃晃的电灯泡,小明很尴尬,就和小刚说,我交给你个任务,你完成了我俩就带你玩,否则你就回家吧.小刚很有当单身狗的觉悟,他坚决不想让小明过好情人节,同为单身狗的你能帮帮他吗?现在有一个n×n(1 <= n <= 1000)的格子,每一个格子都有一个电灯泡,可能是亮的,也可能是灭的(1代表亮, 0代表灭),现在有两

【2018年全国多校算法寒假训练营练习比赛(第三场)】

之前题目比较水,今天的还可以. [A 不凡的大夫] 方法一:答案是log8(n!),解决方案是预处理,将需要的答案记录下来以免超内存: 方法二:用公式,斯特林公式:    [B 一个小问题] 题解:线性同余方程组,一看就不是中国剩余定理,当心. [C 守护白起] 题解:polya..... [D 小牛vs小客] 题解:博弈,做过不久. [E 进击吧!阶乘] 题解:高精度裸题. [F 小牛再战] 题解:博弈,看看第一步能不能控制住对方. [G 大水题] 题解:简单容斥定理. [H 向左走] 一眼不