• 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
    ");
        else printf("Yes
    ");
      }
      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
    ");
          continue;
        }
        for(int i = 0; i < sz; i ++) {
          printf("%d", ans[i]);
          if(i < sz -  1) printf(" ");
          else printf("
    ");
        }
      }
      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
    ", 0);
          return 0;
        }
        if(sum1 == sum3 && sum2 == sum3) {
          printf("%d
    ", sum1 - 1);
        }
        else
        printf("%d
    ", 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
    ", 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
    ");
      else {
        cnt = 0;
        for (i = 1; i <= n; ++i)
          if (id[i] == p) ++cnt;
        printf("%d
    ", 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
    ");
        else printf("No
    ");
      }
      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("
    ");
      }
       */
      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;
    }
    
  • 相关阅读:
    markdown模式的一些语法
    markdown模式的一些语法
    微信小游戏跳一跳外挂教程(安卓版)
    微信小游戏跳一跳外挂教程(安卓版)
    仿百度地图上拉下滑抽屉盒
    仿百度地图上拉下滑抽屉盒
    验证码倒计时的注册页面
    验证码倒计时的注册页面
    1430 素数判定
    2834 斐波那契数
  • 原文地址:https://www.cnblogs.com/zufezzt/p/8442724.html
Copyright © 2020-2023  润新知