PAT : 团体程序设计天梯赛-练习集L2 个人题解

另把天梯赛所有题解内容全部打包成了一个文档,可以自行下载:https://download.csdn.net/download/daixinliangwyx/11170075

L2-001 城市间紧急救援 

解法:裸迪杰斯特拉最短路径+打印路径

代码:

#include
#define inf 0x3f3f3f3f
using namespace std;
int n, m, s, d, u, v, len;
int to[510], dis[510][510], val[510], sumpath[510], path[510], sum[510], vis[510], pre[510];
void Dijstr() {
  to[s] = 0;
  for(int i = 0; i < n; i++) {
    int minn = inf, next = -1;
    for(int j = 0; j < n; j++) {
      if(vis[j] == 0 && to[j] < minn) {
        minn = to[j];
        next = j;
      }
    }
    if(next == -1) break;
    else
      vis[next] = 1;
    for(int j = 0; j < n; j++) {
      if(vis[j] == 0 && to[next] + dis[next][j] < to[j]) {
        to[j] = to[next] + dis[next][j];
        sumpath[j] = sumpath[next];//中间点是next
        sum[j] = sum[next] + val[j]; 
        pre[j] = next;
      } else if(vis[j] == 0 && to[next] + dis[next][j] == to[j]) {
        sumpath[j]= sumpath[j] + sumpath[next];
        if(sum[j] < sum[next] + val[j]) {
          sum[j] = sum[next] + val[j];
          pre[j] = next;
        }
      }
    }
  }
}
int main() {
  cin >> n >> m >> s >> d;
  for(int i = 0; i <  n; i++) {
    for(int j = 0; j < n; j++) {
      dis[i][j] = inf;
    }
  }
  for(int i = 0; i < n; i++) {
    scanf("%d", &val[i]);
    sum[i] = val[i];
    sumpath[i] = 1;
    to[i] = inf;
  }
  for(int i = 0; i < m; i++) {
    scanf("%d%d%d", &u, &v, &len);
    dis[u][v] = len;
    dis[v][u] = len;
  }
  Dijstr();
  printf("%d %d\n", sumpath[d], sum[d]);
  int t = d, k = 0;
  path[k++] = d;
  while(t != s) {
    t = pre[t];
    path[k++] = t;
  } 
  for(int i = k-1; i >= 0; i--) {
    printf(i == k-1 ? "%d" : " %d", path[i]);
  }
  printf("\n");
  return 0;
}

L2-002 链表去重

解法:思维题,拿结构体去模拟

代码:

#include
using namespace std;
struct lianbiao {
  int now, num, next, k;
}l[200010];
int vis[100010];
int cmp(lianbiao a, lianbiao b) {
  if(a.k < b.k) return 1;
  return 0;
}
int main() {
  int maxn = 100000;
  int begin, n, a, b, c;
  scanf("%d%d", &begin, &n);
  for(int i = 0; i < maxn*2; i++) {
    l[i].k = maxn * 2;
  }
  int num1 = 0, num2 = 0;
  for(int i = 0; i < n; i++) {
    scanf("%d%d%d", &a, &b, &c);
    l[a].now = a;
    l[a].num = b;
    l[a].next = c;
  }
  for(int i = begin; i != -1; i = l[i].next) {
    if(vis[abs(l[i].num)] == 0) {
      l[i].k = num1;
      vis[abs(l[i].num)] = 1;
      num1++;
    } else {
      l[i].k = maxn + num2;
      num2++;
    }
  }
  sort(l, l+maxn*2, cmp);
  for(int i = 0; i < num1+num2; i++) {
    if(i == num1+num2-1) printf("%05d %d -1\n", l[i].now, l[i].num);
    else if(i == num1-1) printf("%05d %d -1\n", l[i].now, l[i].num);
    else
      printf("%05d %d %05d\n", l[i].now, l[i].num, l[i+1].now);
  }
  return 0;
}

L2-003 月饼

解法:结构体排序

坑点:最好也有double存库存和总价,否则可能会因为浮点误差WA在测试点2。

代码:

#include
using namespace std;
int n, d;
double ans = 0;
struct yuebing {
  double sum, value;
}y[1010];
int cmp(yuebing a, yuebing b) {
  if(a.value/a.sum > b.value/b.sum) return 1;
  return 0;
}
int main() {
  cin >> n >> d;
  for(int i = 0; i < n; i++)
    cin >> y[i].sum;
  for(int i = 0; i < n; i++)
    cin >> y[i].value;
  sort(y, y+n, cmp);
  for(int i = 0; i < n; i++) {
    if(d == 0) break;
    if(d >= y[i].sum) {
      ans += y[i].value;
      d -= y[i].sum;
    } else {
      ans += y[i].value*d/y[i].sum;
      d = 0;
    }
  }
  printf("%.2lf\n", ans);
  return 0;
}

L2-004 搜索树判断 

解法:二叉树建树、遍历

代码:(自己太菜了只拿了17分,后续有空再补补这题吧)

#include
using namespace std;
int a[1010], tree[1010], f[1010], k;
int flag1, flag2, ceng;
void build(int l, int r) {
  if(l > r) return;
  tree[k++] = a[l];
  int t = (r - l) / 2;
  build(l+t+1, r);
  build(l+1, l+t);
}
void judgebuild1(int l, int r) {//判断是不是左小右大 
  int t = (r - l) / 2;
  int ll = a[l+1];
  int rr = a[l+t+1];
  if(f[l+1]) return;//如果左右孩子已被使用就返回 
  f[l+1] = 1;//记录左右孩子已被使用 
  if(f[l+t+1]) return;
  f[l+t+1] = 1;
  if(ll >= a[l] || rr < a[l]) flag1 = 1;
  judgebuild1(l+1, l+t);
  judgebuild1(l+t+1, r);
}
void judgebuild2(int l, int r) {
  int t = (r - l) / 2;
  int ll = a[l+1];
  int rr = a[l+t+1];
  if(f[l+1]) return;
  f[l+1] = 1;
  if(f[l+t+1]) return;
  f[l+t+1] = 1;
  if(ll < a[l] || rr >= a[l]) flag2 = 1;
  judgebuild1(l+1, l+t);
  judgebuild1(l+t+1, r);
}
int main() {
  int n;
  flag1 = 0;
  flag2 = 0;
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  ceng = 0;
  for(int i = 0; i <= n; i++) {
    int tmp = pow(2, i);
    if(tmp > n) break;
    ceng++;
  }
  k = 0;
  memset(f, 0, sizeof(f));
  f[1] = 1;
  judgebuild1(1, n);
  memset(f, 0, sizeof(f));
  f[1] = 1;
  judgebuild2(1, n);
  if(flag1 && flag2) printf("NO\n");
  else {
    build(1, n);
    printf("YES\n");
    for(int i = k-1; i >= 0; i--) {
      printf(i == k-1 ? "%d" : " %d", tree[i]);
    }
    printf("\n");
  }
  return 0;
}

L2-005 集合相似度

解法:nc就是两个集合分别去重后交集的元素个数,nt就是两个集合直接并起来再去重后的元素个数。

坑点:每个集合先预处理存进set里,算nc时应该遍历集合1,然后用集合2调find方法来查找是否有集合1的某项值(即交集),查找速度O(logN),算nt时就可以用两个集合总元素个数-两个集合交集元素个数,就不需要遍历然后全部插进set了,否则会超时。

代码:

#include
using namespace std;
int n, k, num, t, a, b, nc, nt;
set s[55];
int main() {
  cin >> n;
  for(int i = 1; i <= n; i++) {
    cin >> k;
    for(int j = 0; j < k; j++) {
      cin >> num;
      s[i].insert(num);
    }
  }
  cin >> t;
  while(t--) {
    nc = 0, nt = 0;
    cin >> a >> b;
    set::iterator it;
    for(it = s[a].begin(); it != s[a].end(); it++) 
      if(s[b].find(*it) != s[b].end()) nc++;
    nt = s[a].size() + s[b].size() - nc;
    printf("%.2lf%\n", nc*100*1.0/nt);
  }
  return 0;
}

L2-006 树的遍历

解法:裸二叉树建树、遍历

代码:

#include
using namespace std;
int a[50], b[50], n;
map L, R;
int build(int l1, int r1, int l2, int r2) {//后序, 中序 
  if(l1 > r1) return 0;
  int root = a[r1];
  int i;
  for(i = l2; i <= r2; i++) {
    if(b[i] == root) break;
  }
  if(i <= r2) {
    int len = i - l2; 
    L[root] = build(l1, l1+len-1, l2, i-1);
    R[root] = build(l1+len, r1-1, i+1, r2);
  }
  return root;
}
void bfs(int root) {
  queue q;
  q.push(root);
  int cnt = 0;
  while(!q.empty()) {
    int num = q.front();
    q.pop();
    printf(cnt == 0 ? "%d" : " %d", num);
    cnt++;
    if(L[num]) q.push(L[num]);
    if(R[num]) q.push(R[num]);
  }
}
int main() {
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  for(int i = 1; i <= n; i++) {
    scanf("%d", &b[i]);
  }
  int root = build(1, n, 1, n);
  bfs(root);
  return 0;
}

L2-007 家庭房产

解法:并查集

代码:

#include
using namespace std;
int n, m, pre[10010], num[10010], area[10010], cc, k[10010], q, u, v, tot[10010], f[10010];
int t, s, coun;
vector children[10010];
int bianhao[10010], fa[10010], mo[10010];
struct family {
  int bh, sum;
  double avet, aves;
}c[10010];
int find(int x) {
  if(x == pre[x]) return pre[x];
  return pre[x] = find(pre[x]);
}
void join(int x, int y) { 
  int fx = find(x), fy = find(y);
  if(fx != fy) {
    if(fx < fy) {//小的点作为根 
      pre[fy] = fx;
      num[fx] += num[fy];
      area[fx] += area[fy];
      tot[fx] += tot[fy];
    } else {
      pre[fx] = fy;
      num[fy] += num[fx];
      area[fy] += area[fx];
      tot[fy] += tot[fx];
    }
  }
}
int cmp(family a, family b) {
  if(a.aves > b.aves) return 1;
  else if(a.aves == b.aves && a.bh < b.bh) return 1;
  return 0;
}
int main() {
  for(int i = 0; i < 10010; i++) {
    pre[i] = i;
    num[i] = 1;
  }
  scanf("%d", &n);
  for(int i = 0; i < n; i++) {
    scanf("%d%d%d%d", &bianhao[i], &fa[i], &mo[i], &k[i]);
    f[bianhao[i]] = 1;
    for(int j = 0; j < k[i]; j++) {
      scanf("%d", &cc);
      children[i].push_back(cc);
      f[cc] = 1;
    }
    scanf("%d%d", &t, &s);
    tot[bianhao[i]] += t;
    area[bianhao[i]] += s;
  }
  for(int i = 0; i < n; i++) {
    if(fa[i] != -1) {
      f[fa[i]] = 1;
      join(bianhao[i], fa[i]);
    }
    if(mo[i] != -1) {
      f[mo[i]] = 1;
      join(bianhao[i], mo[i]);
    }
    for(int j = 0; j < k[i]; j++) {
      join(bianhao[i], children[i][j]);
    }
  }
  coun = 0;
  for(int i = 0; i < 10010; i++) {
    if(f[find(i)]) {
      c[coun].bh = find(i);
      c[coun].sum = num[find(i)];
      c[coun].avet = tot[find(i)] * 1.0 / num[find(i)];
      c[coun].aves = area[find(i)] * 1.0 / num[find(i)];
      coun++;
      f[find(i)] = 0;
    }
  }
  sort(c, c+coun, cmp);
  printf("%d\n", coun);
  for(int i = 0; i < coun; i++) {
    printf("%04d %d %.3lf %.3lf\n", c[i].bh, c[i].sum, c[i].avet, c[i].aves);
  }
  return 0;
}

L2-008 最长对称子串

解法:模拟

代码:

#include
using namespace std;
int main() {
  string s;
  getline(cin, s);
  int maxlen = 1, slen = s.size(), flag = 0;
  for(int len = slen; len > 1; len--) {
    for (int i = 0; i < slen-len+1; i++) {
      int p = 0;
      for(int j = i; j < i+len; j++) {
        if(s[j] != s[i+len-(j-i+1)]) {
          p = 1;
          break;
        }
      }
      if(p == 0) {
        flag = 1;
        maxlen = len;
        break;
      }
    }
    if (flag == 1) break;
  }
  printf("%d\n", maxlen);
  return 0;
}

L2-009 抢红包

解法:模拟,计算

代码:

#include
using namespace std;
struct hongbao {
  int id, num;
  double get;
} h[10005];
int cmp(hongbao a, hongbao b) {
  if(a.get > b.get) return 1;
  if(a.get == b.get && a.num > b.num) return 1;
  if(a.get == b.get && a.num == b.num && a.id < b.id) return 1;
  return 0;
}
int main() {
  int n, k, a, b;
  cin >> n;
  for(int i = 0; i <= n; i++) {
    h[i].id = i;
    h[i].num = 0;
    h[i].get = 0;
  }
  for(int i = 1; i <= n; i++) {
    cin >> k;
    for(int j = 0; j < k; j++) {
      cin >> a >> b;
      h[a].get += b;
      h[i].get -= b;
      h[a].num++;
    }
  }
  sort(h+1, h+n+1, cmp);
  for(int i = 1; i <= n; i++)
    printf("%d %.2lf\n", h[i].id, h[i].get/100);
  return 0;
}

L2-010 排座位

解法:模拟

代码:

#include
using namespace std;
int a[110][110], n, m, k, u, v, r;
int pre[110];
int find(int x) {
  if(x == pre[x]) return pre[x];
  return pre[x] = find(pre[x]);
}
void join(int x, int y) {
  int fx = find(x), fy = find(y);
  if(fx != fy) {
    pre[fx] = fy;
  }
}
int main() {
  scanf("%d%d%d", &n, &m, &k);
  for(int i = 1; i <= n; i++) {
    pre[i] = i;
  }
  for(int i = 0; i < m; i++) {
    scanf("%d%d%d", &u, &v, &r);
    a[u][v] = r;
    a[v][u] = r;
    if(r == 1) join(u, v);
  }
  while(k--) {
    scanf("%d%d", &u, &v);
    if(a[u][v] == -1) {//有敌对关系 
      if(find(u) == find(v)) printf("OK but...\n");
      else
        printf("No way\n");
    } else {
      if(find(u) == find(v)) printf("No problem\n");
      else
        printf("OK\n");
    }
  }
  return 0;
}

L2-011 玩转二叉树

解法:裸二叉树建树、遍历

代码:

#include
using namespace std;
int a[50], b[50], n;
map L, R;
int build(int l1, int r1, int l2, int r2) {//中序, 前序 
  if(l1 > r1) return 0;
  int root = b[l2];
  int i;
  for(i = l1; i <= r1; i++) {
    if(a[i] == root) break;
  }
  if(i <= r1) {
    int len = i - l1; 
    L[root] = build(i+1, r1, l2+len+1, r2);
    R[root] = build(l1, i-1, l2+1, l2+len);
  }
  return root;
}
void bfs(int root) {
  queue q;
  q.push(root);
  int cnt = 0;
  while(!q.empty()) {
    int num = q.front();
    q.pop();
    printf(cnt == 0 ? "%d" : " %d", num);
    cnt++;
    if(L[num]) q.push(L[num]);
    if(R[num]) q.push(R[num]);
  }
}
int main() {
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  for(int i = 1; i <= n; i++) {
    scanf("%d", &b[i]);
  }
  int root = build(1, n, 1, n);
  bfs(root);
  return 0;
}

L2-012 关于堆的判断

解法:太难了,后续有时间再补

代码:无

L2-013 红色警报 

解法:并查集

代码:

#include
using namespace std;
int n, m, pre[510], d[510], path[510][510], u,  v, q, city, f[510], a[5100], b[5100];
int find(int x) {
  if(x == pre[x]) return pre[x];
  return pre[x] = find(pre[x]);
}
void join(int x, int y) {
  int fx = find(x), fy = find(y);
  if(fx != fy) {
    pre[fx] = fy;
  }
}
int main() {
  scanf("%d%d", &n, &m);
  for(int i = 0; i < n; i++) {
    pre[i] = i;
    f[i] = 0;
  }
  for(int i = 0; i < m; i++) {
    scanf("%d%d", &u, &v);
    a[i] = u;
    b[i] = v;
    join(u, v);
  }
  set s;
  for(int i = 0; i < n; i++) {
    s.insert(find(i));
  }
  scanf("%d", &q);
  int sum = q;
  while(q--) {
    scanf("%d", &city);
    int old = s.size();
    f[city] = 1;
    s.clear();
    for(int i = 0; i < n; i++) {
      pre[i] = i;
    }
    for(int i = 0; i < m; i++) {
      if(f[a[i]] == 1 || f[b[i]] == 1) continue;
      join(a[i], b[i]);
    }
    for(int i = 0; i < n; i++) {
      s.insert(find(i));
    }
    if(s.size() - old > 1) printf("Red Alert: City %d is lost!\n", city);
    else {
      printf("City %d is lost.\n", city);
    }
  }
  if(s.size() == n) printf("Game Over.\n");
  return 0;
}

L2-014 列车调度

解法:模拟

代码:

#include
using namespace std;
int a[100010];
int main() {
  int n;
  set s;
  scanf("%d", &n);
  for(int i = 0; i < n; i++) {
    scanf("%d", &a[i]);
  }
  for(int i = 0; i < n; i++) {
    if(s.upper_bound(a[i]) != s.end()) {
      s.erase(s.upper_bound(a[i]));
    }
    s.insert(a[i]);
  }
  printf("%d\n", s.size());
  return 0;
}

L2-015 互评成绩 

解法:模拟

代码:

#include
using namespace std;
int n, k, m, sum, maxn, minn, score;
double ans[10010];
int main() {
  cin >> n >> k >> m;
  for(int i = 0; i < n; i++) {
    sum = 0;
    maxn = 0;
    minn = 100;
    for(int j = 0; j < k; j++) {
      cin >> score;
      sum += score;
      maxn = max(maxn, score);
      minn = min(minn, score);
    }
    ans[i] = (sum - maxn - minn) * 1.0 / (k - 2);
  }
  sort(ans, ans+n);
  for(int i = n-m; i < n; i++)
    printf(i == n-m ? "%.3lf" : " %.3lf", ans[i]);
  return 0;
}

L2-016 愿天下有情人都是失散多年的兄妹

解法:递归分治

代码:

#include
using namespace std;
int f[100000], m[100000], x[100000];
int flag;
map qinqi;
void judge(int num, int k) {
  if(k >= 5) return;
  if(f[num] != -1) {
    qinqi[f[num]] = 1;
    judge(f[num], k+1);
  }
  if(m[num] != -1) {
    qinqi[m[num]] = 1;
    judge(m[num], k+1);
  }
}
int judgeother(int num, int k) {
  if(k >= 5) return 0;
  if(f[num] != -1) {
    if(qinqi[f[num]] == 1) return 1;
    if(judgeother(f[num], k+1) == 1) return 1;
  }
  if(m[num] != -1) {
    if(qinqi[m[num]] == 1) return 1;
    if(judgeother(m[num], k+1) == 1) return 1;
  }
  return 0;
}
int main() {
  int n, q, a, b, p, fa, mo;
  memset(f, -1, sizeof(f));
  memset(m, -1, sizeof(m));
  char sex;
  scanf("%d", &n);
  for(int i = 0; i < n; i++) {
    scanf("%05d %c %05d %05d", &p, &sex, &fa, &mo);
    f[p] = fa;
    m[p] = mo;
    if(sex == 'M') x[p] = 1;
    else
      x[p] = 0;
    if(fa != -1) x[fa] = 1;
    if(mo != -1) x[mo] = 0;
  }
  scanf("%d", &q);
  while(q--) {
    scanf("%d%d", &a, &b);
    if(x[a] == x[b]) printf("Never Mind\n");
    else {
      flag = 0;
      qinqi.clear();
      judge(a, 1);
      if(judgeother(b, 1) == 1) printf("No\n");
      else
        printf("Yes\n");
    }
  }
  return 0;
}

L2-017 人以群分

解法:模拟

代码:

#include
using namespace std;
int a[100010];
int main() {
  int n, t, sum1 = 0, sum2 = 0;
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  sort(a+1, a+n+1);
  if(n % 2 == 0) {
    for(int i = 1; i <= n; i++) {
      if(i <= n/2) sum1 += a[i];
      else
        sum2 += a[i];
    }
    printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d\n", n/2, n/2, sum2-sum1);
  } else {
    for(int i = 1; i <= n; i++) {
      if(i <= n/2) sum1 += a[i];
      else
        sum2 += a[i];
    }
    printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d\n", n-n/2, n/2, sum2-sum1);
  }
  return 0;
}

L2-018 多项式A除以B

解法:数学计算题,告辞

代码:无

L2-019 悄悄关注

解法:模拟

代码:

#include
using namespace std;
int main() {
  int n, m, sum = 0, k, flag = 0;
  string name;
  mapyonghu;
  cin >> n;
  for(int i = 0; i < n; i++) {
    cin >> name;
    yonghu[name] = true;
  }
  cin >> m;
  mapdianzan;
  for(int i = 0; i < m; i++) {
    cin >> name >> k;
    dianzan[name] = k;
    sum += k;
  }
  map::iterator iter;
  for(iter = dianzan.begin(); iter != dianzan.end(); iter++)
    if(iter->second > sum*1.0/m && yonghu[iter->first] != true) {
      flag = 1;
      cout << iter->first << endl;
    }
  if(!flag) cout << "Bing Mei You" << endl;
  return 0;
}

L2-020 功夫传人 

解法:递归

代码:

#include
using namespace std;
int dedao[100010];
vector v[100010];//存徒弟 
int n, k, num;
double z, r, sum;
void dfs(int id, double tmp) {
  int len = v[id].size();
  for(int i = 0; i < len; i++) {
    if(dedao[v[id][i]]) {//得道
      sum += tmp*(100-r)*dedao[v[id][i]]/100; 
    } else {
      dfs(v[id][i], tmp*(100-r)/100);
    }
  }
}
int main() {
  sum = 0;
  scanf("%d%lf%lf", &n, &z, &r);
  for(int i = 0; i < n; i++) {
    scanf("%d", &k);
    if(k == 0) {
      scanf("%d", &num);
      dedao[i] = num;
    } else {
      for(int j = 0; j < k; j++) {
        scanf("%d", &num);
        v[i].push_back(num);
      }
    }
  }
  if(dedao[0] != 0) sum += dedao[0] * z;//祖师爷得道 
  dfs(0, z);
  printf("%d\n", (int)sum);
  return 0;
}

L2-021 点赞狂魔 

解法:模拟

代码:

#include
using namespace std;
int n, k, id;
struct dianzan {
  string name;
  int sum;
  double avg;
}d[105];
int cmp(dianzan a, dianzan b) {
  if(a.sum > b.sum) return 1;
  else if(a.sum == b.sum && a.avg < b.avg) return 1;
  return 0;
}
int main() {
  cin >> n;
  for(int i = 0; i < n; i++) {
    cin >> d[i].name >> k;
    set s;
    for(int j = 0; j < k; j++) {
      cin >> id;
      s.insert(id);
    }
    d[i].sum = s.size();
    d[i].avg = k * 1.0 / s.size();
  }
  sort(d, d+n, cmp);
  if(n < 3) {
    cout << d[0].name;
    int i;
    for(i = 1; i < n; i++)
      cout << " " << d[i].name;
    for(; i < 3; i++)
      cout << " -";
  } else {
    cout << d[0].name;
    for(int i = 1; i < 3; i++) 
      cout << " " << d[i].name;
  }
  return 0;
}

L2-022 重排链表 

解法:思维题,结构体模拟

代码:

#include
using namespace std;
struct lianbiao {
  int now, num, next, id;
} l[100010];
int cmp(lianbiao a, lianbiao b) {
  if(a.id < b.id) return 1;
  return 0;
}
int main() {
  int begin, n, k = 1, now, num, next;
  scanf("%d%d", &begin, &n);
  for(int i = 0; i < 100010; i++) {
    l[i].id = 200000;
  }
  for(int i = 0; i < n; i++) {
    scanf("%d%d%d", &now, &num, &next);
    l[now].now = now;
    l[now].num = num;
    l[now].next = next;
  }
  for(int i = begin; i != -1; i = l[i].next) {
    l[i].id = k;
    k++;
  }
  sort(l, l+100010, cmp);
  for(int i = 0; i < k-1; i++) {
    if(i % 2 == 0) {
      if(i == k-2) printf("%05d %d -1\n", l[k-2-i/2].now, l[k-2-i/2].num);
      else
        printf("%05d %d %05d\n", l[k-2-i/2].now, l[k-2-i/2].num, l[(i+1)/2].now);
    } else {
      if(i == k-2) printf("%05d %d -1\n", l[i/2].now, l[i/2].num);
      else
        printf("%05d %d %05d\n", l[i/2].now, l[i/2].num, l[k-2-(i+1)/2].now);
    }
  }
  return 0;
}

L2-023 图着色问题 

解法:思维题

代码:

#include
using namespace std;
vector v[510];
int color[510];
int main() {
  int n, e, k, q, u1, u2;
  scanf("%d%d%d", &n, &e, &k);
  for(int i = 0; i < e; i++) {
    scanf("%d%d", &u1, &u2);
    v[u1].push_back(u2);
    v[u2].push_back(u1);
  }
  scanf("%d", &q);
  while(q--) {
    set s;
    for(int i = 1; i <= n; i++) {
      scanf("%d", &color[i]);
      s.insert(color[i]);
    }
    if(s.size() != k) {
      printf("No\n");
      continue;
    } else {
      int flag = 0;
      for(int i = 1; i <= n; i++) {
        int len = v[i].size();
        for(int j = 0; j < len; j++) {
          if(color[i] == color[v[i][j]]) {
            flag = 1;
            break;
          }
        }
        if(flag) break;
      }
      if(flag) printf("No\n");
      else
        printf("Yes\n");
    }
  }
  return 0;
}

L2-024 部落 

解法:裸并查集

代码:

#include
using namespace std;
int n, pre[10010], a[10010], k, q, u, v, f[10010];
int find(int x) {
  if(x == pre[x]) return pre[x];
  return pre[x] = find(pre[x]);
}
void join(int x, int y) {
  int fx = find(x), fy = find(y);
  if(fx != fy) {
    pre[fx] = fy;
  }
}
int main() {
  int sum = 0;
  scanf("%d", &n);
  for(int i = 0; i < 10010; i++) {
    pre[i] = i;
  }
  for(int i = 0; i < n; i++) {
    scanf("%d", &k);
    for(int j = 0; j < k; j++) {
      scanf("%d", &a[j]);
      if(f[a[j]] == 0) {
        f[a[j]] = 1;
        sum++;
      }
      if(j > 0) join(a[j], a[j-1]);
    }
  }
  set s;
  for(int i = 0; i < 10010; i++) {
    if(f[i]) s.insert(find(i));
  }
  printf("%d %d\n", sum, s.size());
  scanf("%d", &q);
  while(q--) {
    scanf("%d%d", &u, &v);
    if(find(u) == find(v)) printf("Y\n");
    else 
      printf("N\n");
  }
  return 0;
}

L2-025 分而治之 

解法:思维题

代码:

#include
using namespace std;
int n, m, pre[10010], a[10010], k, q, u, v, f[10010], num, t;
struct city {
  int x, y;
} c[10010];
int main() {
  int sum = 0;
  scanf("%d%d", &n, &m);
  for(int i = 0; i < m; i++) {
    scanf("%d%d", &c[i].x, &c[i].y);
  }
  scanf("%d", &q);
  while(q--) {
    memset(f, 0, sizeof(f));
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
      scanf("%d", &t);
      f[t] = 1;
    }
    int cnt = 0;
    for(int i = 0; i < m; i++) {
      if(f[c[i].x] == 1 || f[c[i].y] == 1) continue;
      cnt++;
    }
    if(cnt == 0) printf("YES\n");
    else
      printf("NO\n");
  }
  return 0;
}

L2-026 小字辈 

解法:简单bfs

代码:

#include
using namespace std;
vector v[100010];
int n, num, root, f[100010], k, maxlevel;
struct people {
  int bianhao;
  int level;
}pp[100010];
void bfs(int level) {
  queue q;
  people now, next;
  now.bianhao = root;
  now.level = 1;
  q.push(now);
  while(!q.empty()) {
    now = q.front();
    if(now.level > maxlevel) {
      maxlevel = now.level;
      k = 0;
    }
    if(now.level == maxlevel) f[k++] = now.bianhao;
    q.pop();
    int len = v[now.bianhao].size();
    for(int i = 0; i < len; i++) {
      next.bianhao = v[now.bianhao][i];
      next.level = now.level + 1;
      q.push(next);
    }
  }
}
int main() {
  scanf("%d", &n);
  maxlevel = 0;
  for(int i = 1; i <= n; i++) {
    scanf("%d", &num);
    if(num == -1) root = i;
    else
      v[num].push_back(i);
  }
  bfs(1);
  printf("%d\n", maxlevel);
  sort(f, f+k); 
  for(int i = 0; i < k; i++) {
    printf(i == 0 ? "%d" : " %d", f[i]);
  }
  printf("\n");
  return 0;
}

L2-027 名人堂与代金券 

解法:结构体排序

代码:

#include
using namespace std;
int a[10010];
struct st {
  string name;
  int score;
  int rank;
} s[10010];
int cmp(st a, st b) {
  if(a.score > b.score) return 1;
  else if(a.score == b.score && a.name < b.name) return 1;
  return 0;
}
int main() {
  int n, g, k, sum = 0;
  scanf("%d%d%d", &n, &g, &k);
  for(int i = 0; i < n; i++) {
    cin >> s[i].name >> s[i].score;
    if(s[i].score >= 60 && s[i].score < g) sum += 20;
    if(s[i].score >= g && s[i].score <= 100) sum += 50;
  }
  sort(s, s+n, cmp);
  printf("%d\n", sum);
  int m = 1, rm = 1, t = 0;
  for(int i = 0; i < n; i++) {
    if(i != 0 && s[i].score != s[i-1].score) m = i + 1;
    s[i].rank = m;
  }
  for(int i = 0; i < n; i++) {
    if(s[i].rank > k) break;
    cout << s[i].rank << " " << s[i].name << " " << s[i].score << endl;
  }
  return 0;
}

L2-028 秀恩爱分得快 

解法:一开始我是算出所有关系对的亲密度然后直接查找,但是时间复杂度是m*k*k,是会超时的。需要换一种方法来避免不必要关系对的计算,就可以事先将每张照片出现的人存起来,我是存进set数组里面,因为后续需要查找某张照片里面是否出现某个人的时候,set的find方法查找速度是比较快的。这里需要预处理每个人的性别存进sex数组,后续可以通过编号来直接获取性别,但是这里要注意0的男女判断,0代表男性,-0代表的是女性,因此输入人的时候需要采用字符输入的方式。给定两个人要分别计算他们与别人的亲密度的时候,只需要遍历每张照片,当该照片中出现了该人的时候才计算亲密度,且只需要计算该人与其他人的亲密度不需要计算其他人与该人的亲密度,这样就不会超时了。

坑点:逻辑没错的话,很可能会出现输出上的坑,特别要注意女性的0在输出时是需要手动加负号的,比如查询输入的a为-0时,a实际存储的是0(不带负号),为了能够输出-0,就需要在输出的时候给女性手动加负号 。另外提供两组测试数据供大家测试:


input:

4 1
4 -0 1 -2 3
-0 1

output:

-0 1


input:

4 2
4 -0 1 -2 3
2 -0 3
1 -0

output:

1 -0
1 -2
-0 3


代码:

#include
using namespace std;
double p, maxwitha = 0, maxwithb = 0, re[1010][1010];
int sex[1010], cnt[1010], ansa[1010], ansb[1010];
set s[1010];
int read() {
  int x = 0, f = 1;
  int ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;//读到了负号 
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + (int)(ch - '0');
    ch = getchar();
  }
  if(f == 1) sex[x] = 1;//男性标为1,女性为默认值0 
  return x;//返回该人的非负编号 
}
int main() {
  int n, m, a, b, num, numa = 0, numb = 0;
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= m; i++) {
    scanf("%d", &cnt[i]);
    for(int j = 0; j < cnt[i]; j++) {
      num = read();
      s[i].insert(num);
    }
  }
  scanf("%d%d", &a, &b);
  for(int i = 1; i <= m; i++) {
    p = 1.0 / cnt[i];
    if(s[i].find(abs(a)) != s[i].end()) {
      set::iterator it;
      for(it = s[i].begin(); it != s[i].end(); it++) 
        if(sex[abs(a)] != sex[*it]) re[abs(a)][*it] += p;
    }
    if(s[i].find(abs(b)) != s[i].end()) {
      set::iterator it;
      for(it = s[i].begin(); it != s[i].end(); it++) 
        if(sex[abs(b)] != sex[*it]) re[abs(b)][*it] += p;
    }
  }
  for(int i = 0; i < n; i++) {
    if(re[abs(a)][i] > maxwitha) {
      maxwitha = re[abs(a)][i];
      numa = 0;
      ansa[numa++] = i;
    } else if(re[abs(a)][i] == maxwitha) {
      ansa[numa++] = i;
    }
    if(re[abs(b)][i] > maxwithb) {
      maxwithb = re[abs(b)][i];
      numb = 0;
      ansb[numb++] = i;
    } else if(re[abs(b)][i] == maxwithb) {
      ansb[numb++] = i;
    }
  }
  //判断a和b是否正是彼此亲密度最高的一对,即与a与别人最高的亲密度刚好等于a与b的亲密度且b与别人最高的亲密度刚好等于b与a的亲密度
  if(re[abs(a)][ansa[0]] == re[abs(a)][abs(b)] && re[abs(b)][ansb[0]] == re[abs(b)][abs(a)]) printf(sex[abs(a)] == 0 ? "-%d %d\n" : "%d -%d\n", abs(a), abs(b));
  else {
    for(int i = 0; i < numa; i++) {
      if(sex[abs(a)]) printf("%d -%d\n", a, ansa[i]);
      else 
        printf("-%d %d\n", abs(a), ansa[i]);
    }
    for(int i = 0; i < numb; i++) {
      if(sex[abs(b)]) printf("%d -%d\n", b, ansb[i]);
      else
        printf("-%d %d\n", abs(b), ansb[i]);
    }
  }
  return 0;
}

L2-029 特立独行的幸福

解法:模拟。对未进行标记的数进行模拟,记录该数迭代后的所有数,当结束迭代时,通过判断最终迭代结果是否为1来确定该数是不是幸福数,如果是,则只将迭代后的所有数标记为0,将该数标记为幸福数;如果不是,就把该数和迭代数都标记为0。至于特立独行性,因为每次在判断被依赖数之后,都会对依赖数标记为0,所以在for循环结束后,就只会保留下特立独行的幸福数(被依赖数)。

坑点:小于10的数里面只有7是特立独行的幸福数。

代码:

#include
using namespace std;
int a, b, tmp, cnt, t, flag, f[10010], vis[10010];
int isprimer(int n) {
  for(int i = 2; i <= sqrt(n); i++)
    if(n % i == 0) return 0;
  return 1;
}
int main() {
  memset(f, -1, sizeof(f));
  cin >> a >> b;
  for(int i = a; i <= b; i++) {
    if(f[i] != -1) continue;
    memset(vis, 0, sizeof(vis));
    vector v;
    tmp = i;
    cnt = 0;
    v.push_back(i);
    do {
      vis[tmp] = 1;
      t = tmp;
      tmp = 0;
      while(t) {
        tmp += (t % 10) * (t % 10);
        t /= 10;
      }
      v.push_back(tmp);
      cnt++;
    } while(!vis[tmp] && tmp != 1);
    if(tmp == 1) {
      f[i] = cnt;
      if(isprimer(i)) f[i] *= 2;
      for(int j = 1; j < v.size(); j++) 
        f[v[j]] = 0;
    } else {
      for(int j = 0; j < v.size(); j++) 
        f[v[j]] = 0;
    }
  }
  flag = 0;
  for(int i = a; i <= b; i++) {
    if(f[i]) {
      flag = 1;
      cout << i << " " << f[i] << endl;
    }
  }
  if(!flag) cout << "SAD" << endl;
  return 0;
}

L2-030 冰岛人 

解法:耐心读懂题意就好了。注意这句话----“所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。”,意思就是说两人的公共祖先不能是任何一人的上三代以内(包括第三代也包括自己)。

做起来的话,因为需要存储每个人的父亲,首先需要给每个人编号,另外寻找父亲的话,由于不能用n^2的方法遍历找父亲(会超时),因此可以用两个map容器来存每个人对应的编号,一个map存名另一个存姓(因为后续查询时的姓是不带后缀的因此可以提取姓后缀子串得到姓)作为键,该人对应的编号作为值。还可以预处理一下每个人的性别,方便后面直接判断。在查询的时候,也可以用一个map来存储祖先编号和祖先代数,这样就可以在发现存在共同祖先的时候,直接通过判断祖先代数来得到答案。

坑点:给定查询的两个人,有可能某个人是另一个人的直系祖先;如果两个人存在公共祖先,公共祖先是一个人的上10代祖先,是另一个人的上1代祖先,那么也是No的。(测试点3、6答案错误)

把cin改成scanf的输入。(测试点6超时)

代码:

#include
using namespace std;
int n, t, pre[100010], sex[100010];
string s1[100010], s2[100010], fn1, ln1, fn2, ln2;
map firstname, lastname;
int main() {
  scanf("%d", &n);
  getchar();
  for(int i = 1; i <= n; i++) {
    cin >> s1[i] >> s2[i];
    firstname[s1[i]] = i;
    if(s2[i].size() > 4 && s2[i].substr(s2[i].size()-4, s2[i].size()) == "sson") {
      sex[i] = 1;
      lastname[s2[i].substr(0, s2[i].size()-4)] = i;
    } else if(s2[i].size() > 7 && s2[i].substr(s2[i].size()-7, s2[i].size()) == "sdottir") {
      lastname[s2[i].substr(0, s2[i].size()-7)] = i;
    } else if(s2[i][s2[i].size()-1] == 'm') {
      sex[i] = 1;
      lastname[s2[i].substr(0, s2[i].size()-1)] = i;
    } else {
      lastname[s2[i].substr(0, s2[i].size()-1)] = i;
    }
  }
  for(int i = 1; i <= n; i++) {
    if(s2[i].size() > 4 && s2[i].substr(s2[i].size()-4, s2[i].size()) == "sson") {
      if(firstname[s2[i].substr(0, s2[i].size()-4)] != 0) pre[i] = firstname[s2[i].substr(0, s2[i].size()-4)];
      else
        pre[i] = -1;
    } else if(s2[i].size() > 7 && s2[i].substr(s2[i].size()-7, s2[i].size()) == "sdottir") {
      if(firstname[s2[i].substr(0, s2[i].size()-7)] != 0) pre[i] = firstname[s2[i].substr(0, s2[i].size()-7)];
      else
        pre[i] = -1;
    } else {
      pre[i] = -1;
    }
  }
  scanf("%d", &t);
  getchar();
  while(t--) {
    cin >> fn1 >> ln1 >> fn2 >> ln2;
    if(!firstname[fn1] || !firstname[fn2] || !lastname[ln1] || !lastname[ln2]) printf("NA\n");
    else if(sex[firstname[fn1]] == sex[firstname[fn2]]) printf("Whatever\n");
    else {
      map f;
      int d = 1, flag = 0, now = pre[firstname[fn1]];
      f[firstname[fn1]] = 1;
      while(now != -1) {
        d++;
        if(now == firstname[fn2]) {//2是1的直系
          flag = 1;
          break;
        } else {
          f[now] = d;
        }
        now = pre[now];
      }
      d = 1;
      now = pre[firstname[fn2]];
      while(now != -1 && !flag) {
        d++;
        if(f[now] > 0 && (f[now] < 5 || d < 5)) {//存在公共祖先,且是某一方的上三代以内
          flag = 1;
          break;
        }
        now = pre[now];
      }
      if(flag) printf("No\n");
      else
        printf("Yes\n");
    }
  }
  return 0;
}

L2-031 深入虎穴

解法:简单bfs,记录每次出队的门,最后一次出队的门则是答案。

坑点:入口不一定是1号门,要判断一下入口门的编号,入口意味着没有门在它前面。

代码:

#include
using namespace std;
int n, k, num, root, ans, f[100010];
vector d[100010];
void bfs(int s) {
  queue q;
  q.push(s);
  while(!q.empty()) {
    int now = q.front();
    q.pop();
    ans = now;
    for(int i = 0; i < d[now].size(); i++) 
      q.push(d[now][i]);
  }
  cout << ans << endl;
}
int main() {
  cin >> n;
  for(int i = 1; i <= n; i++) {
    cin >> k;
    for(int j = 0; j < k; j++) {
      cin >> num;
      d[i].push_back(num);
      f[num] = 1;
    }
  }
  for(int i = 1; i <= n; i++) 
    if(!f[i]) {
      root = i;
      break;
    }
  bfs(root);
  return 0;
}

L2-032 彩虹瓶

解法:用栈模拟

代码:

#include
using namespace std;
int n, m, num, now, k, flag, vis[1010];
int main() {
  cin >> n >> m >> k;
  while(k--) {
    memset(vis, 0, sizeof(vis));
    flag = 0;  
    stack s;
    now = 1;
    for(int i = 0; i < n; i++) {
      cin >> num;
      if(flag) continue;
      if(num == now) {
        now++;
        while(!s.empty() && now == s.top()) {
          now++;
          s.pop();
        }
        if(vis[now]) flag = 1;
      } else {
        if(s.size() >= m) flag = 1;
        else {
          s.push(num);
          vis[num] = 1;
        }
      }
    }
    if(flag) cout << "NO" << endl;
    else
      cout << "YES" << endl;
  }
  return 0;
}

 

你可能感兴趣的:(天梯赛)