2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) 解题报告



比赛链接

1:这场比赛 GYM 上的题号与题目上的不一样,这里以 GYM 内提交的题号为准
2:题目顺序为我个人提交的通过顺序,因为这场 GYM 是我一个人打的所以开题的顺序会比较奇怪

题目分析

A. Alphabet

因为可以在任意位置免费删除字符,并且添加字符也可以在任意位置进行。所以答案就是 26 - LIS(s)

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 55;

char s[maxn];
int dp[maxn];

int main() {
  scanf("%s", s + 1);
  int n = strlen(s + 1), ans = 0;
  FOR(i, 1, n) dp[i] = 1;
  FOR(i, 2, n) FOR(j, 1, i - 1) if (s[i] > s[j]) chkmax(dp[i], dp[j] + 1);
  FOR(i, 1, n) chkmax(ans, dp[i]);
  printf("%d", 26 - ans);
}


F. Equality

签到题。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

int a, b, c;

int main() {
  scanf("%d + %d = %d", &a, &b, &c);
  puts(a + b == c ? "YES" : "NO");
}


G. Gravity

模拟。不过我觉得可能有 O(n × m) 的做法?

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 55;

char mat[maxn][maxn];
int n, m;

int main() {
  scanf("%d%d", &n, &m);
  FOR(i, 1, n) scanf("%s", mat[i] + 1);
  FOR(i, 1, m) {
    int en = n;
    ROF(j, n, 1) {
      if (mat[j][i] == '#') en = j - 1;
      else if (mat[j][i] == 'o')
        ROF(k, en, j + 1) if (mat[k][i] == '.') {
            swap(mat[k][i], mat[j][i]);
            break;
          }
    }
  }
  FOR(i, 1, n) printf("%s\n", mat[i] + 1);
}


K. Six Sides

暴力。因为每个骰子只有 6 个面,所以我们可以遍历所有可能的 6 × 6 种情况,用第一个人获胜的情况数除以非平局情况数就可以了。
但是这个题数据可能比较弱,因为按照题目的定义,我们要计算的是第一个人的获胜概率。如果两个骰子上 12 个面的所有数字都一样,这个游戏不会结束,所以第一个人也不会获胜?然而没有特判的提交也通过了。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

int d[2][6], cnt[2];

int main() {
  REP(i, 2) REP(j, 6) scanf("%d", &d[i][j]);
  REP(i, 6) REP(j, 6) {
      if (d[0][i] == d[1][j]) continue;
      if (d[0][i] > d[1][j]) cnt[0]++;
      cnt[1]++;
    }
  if (!cnt[1]) cnt[1]++;
  printf("%.5lf", double(cnt[0]) / cnt[1]);
}


M. Zigzag

动态规划。用 dp[i][0/1] 表示以 a[i] 结尾的 Zigzag 子序列其中最后一次更新是下降/上升的最大长度。于是原来的问题就被转化为两个独立的类 LIS 问题。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 55;

int a[maxn], dp[maxn][2], n, ans;

int main() {
  scanf("%d", &n);
  FOR(i, 1, n) scanf("%d", a + i);
  FOR(i, 1, n) {
    dp[1][0] = dp[1][1] = 1;
    FOR(j, 1, i - 1){
      if (a[j] > a[i]) chkmax(dp[i][0], dp[j][1] + 1);
      if (a[j] < a[i]) chkmax(dp[i][1], dp[j][0] + 1);
    }
  }
  FOR(i, 1, n) REP(j, 2) chkmax(ans, dp[i][j]);
  printf("%d", ans);
}


H. Islands

这个题的难点在于如何把战争迷雾染成陆地或者是水域。如果把边界全部看成水域的话,对于任意一片战争迷雾,其可能的边界情况是:

  • 全部边界都是水域
  • 边界有水域有陆地
  • 全部边界都是陆地

显然对于第一种情况,我们希望这片战争迷雾完全是水域,不然会平添一个岛屿。对于后两种情况,如果把这片战争迷雾全部染色成陆地的话,无论如何不会增加新的岛屿。相反地,如果该战争迷雾与两片(或多片)本不连通的陆地邻接,那么这种染法可能会减少岛屿的个数。
染色以后搜索就可以了。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 55;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};

char mat[maxn][maxn];
int vis[maxn][maxn], clk, n, m;
bool flag[maxn * maxn];

void dfs1(int x, int y) {
  if (vis[x][y]) return;
  vis[x][y] = clk;
  REP(i, 4) {
    int nx = x + dx[i], ny = y + dy[i];
    if (mat[nx][ny] == 'L') flag[clk] = true;
    if (mat[nx][ny] == 'C') dfs1(nx, ny);
  }
}

void dfs2(int x, int y) {
  if (vis[x][y]) return;
  vis[x][y] = 1;
  REP(i, 4) {
    int nx = x + dx[i], ny = y + dy[i];
    if (mat[nx][ny] == 'L') dfs2(nx, ny);
  }
}

int main() {
  scanf("%d%d", &n, &m);
  reset(mat, 'W');
  FOR(i, 1, n) scanf("%s", mat[i] + 1);
  FOR(i, 1, n) FOR(j, 1, m) if (mat[i][j] == 'C' && !vis[i][j]) {
        clk++;
        dfs1(i, j);
      }
  FOR(i, 1, n) FOR(j, 1, m) if (flag[vis[i][j]]) mat[i][j] = 'L';
  reset(vis, 0);
  int ans = 0;
  FOR(i, 1, n) FOR(j, 1, m) if (mat[i][j] == 'L' && !vis[i][j]) {
        ans++;
        dfs2(i, j);
      }
  printf("%d", ans);
}


B. Barbells

暴力。因为 m 很小,不超过 14 ,所以子集生成或者 dfs 都可以处理出可能的杠铃片重量。然后再枚举杠铃杆即可。复杂度 O(m × 3m + mn × 2m)

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

int b[15], p[15], n, m;
set lis, ans;

void dfs(int i, ll l, ll r) {
  if (i > m) return;
  if (l == r) lis.insert(r + l);
  dfs(i + 1, l + p[i + 1], r);
  dfs(i + 1, l, r + p[i + 1]);
  dfs(i + 1, l, r);
}

int main() {
  scanf("%d%d", &n, &m);
  FOR(i, 1, n) scanf("%d", b + i);
  FOR(i, 1, m) scanf("%d", p + i);
  dfs(0, 0, 0);
  FOR(i, 1, n) for (auto it : lis) ans.insert(it + b[i]);
  for (auto it : ans) printf("%lld\n", it);
}


E. Contest Score

std::multiset 模拟即可。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 312;

int n, k, t[maxn];

int main() {
  scanf("%d%d", &n, &k);
  FOR(i, 1, n) scanf("%d", t + i);
  multiset tt;
  FOR(i, 1, k) tt.insert(t[i]);
  ll ans = 0, pre = 0;
  while (!tt.empty()) {
    pre += *tt.begin();
    ans += pre;
    tt.erase(tt.find(*tt.begin()));
    if (k < n) tt.insert(t[++k]);
  }
  printf("%lld", ans);
}


D. Cameras

从前往后检查每个长度为 r 的窗口,滑动地维护窗口内的监控数目。如果发现当前窗口内的监控数目少于两个,那么贪心地从后往前把窗口内的监控数目补足两个即可。复杂度 O(n)

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 112345;

int k, r, n;
bool vis[maxn];

int main() {
  scanf("%d%d%d", &n, &k, &r);
  REP(i, k) {
    int p;
    scanf("%d", &p);
    vis[p] = true;
  }
  int cnt = 0, ans = 0;
  FOR(i, 1, r - 1) cnt += vis[i];
  FOR(i, r, n) {
    cnt -= vis[i - r];
    cnt += vis[i];
    ROF(j, i, 1) {
      if (cnt >= 2) break;
      if (vis[j]) continue;
      vis[j] = true;
      cnt++, ans++;
    }
  }
  printf("%d", ans);
}


L. Three Square

没有什么技巧性的暴力。比较恶心。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

pii lis[5];

int main() {
  int s = 0;
  REP(i, 3) {
    scanf("%d%d", &lis[i]._1, &lis[i]._2);
    if (lis[i]._1 < lis[i]._2) swap(lis[i]._1, lis[i]._2);
    s += lis[i]._1 * lis[i]._2;
  }
  int a = sqrt(s) + 0.5;
  if (a * a != s) {
    puts("NO");
    return 0;
  }
  sort(lis, lis + 3, greater());
  bool ok = false;
  if (lis[0]._1 == a) {
    int b = a - lis[0]._2;
    if (lis[1]._1 == lis[2]._1 && lis[1]._1 == a && lis[1]._2 + lis[2]._2 == b) 
      ok = true;
    else if (lis[1]._1 == lis[2]._1 && lis[1]._1 == b && lis[1]._2 + lis[2]._2 == a) 
      ok = true;
    else if (lis[1]._2 == lis[2]._2 && lis[1]._2 == b && lis[1]._1 + lis[2]._1 == a) 
      ok = true;
    else if (lis[1]._2 == lis[2]._1 && lis[1]._2 == b && lis[1]._1 + lis[2]._2 == a) 
      ok = true;
  }
  puts(ok ? "YES" : "NO");
}


I. Mismatched Socks

我们先找出最多的那种袜子,如果最多的袜子数量大于等于别的袜子的数量和,那么尽可能配对以后输出答案就可以了。如果小于,那么我们把最多的袜子放在左边,然后把其他袜子放在右边,之后尽可能地把右边的某种袜子全部移到左边但同时保证左边袜子数量不超过右边。这个过程结束后左边的任意一只袜子都和右边所有袜子的颜色不一样。之后我任意挑选右边的一种袜子,然后把它们尽可能移动到左边但保证左边袜子数量不超过右边。我们先用右边该颜色剩下的袜子与左边最多的袜子配对,一定可以把右边的这部分袜子用完。之后任意左右配对即可。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 1123;

ll lis[maxn];
int n;

int main() {
  scanf("%d", &n);
  FOR(i, 1, n) scanf("%lld", &lis[i]);
  sort(lis + 1, lis + n + 1);
  ll mm = 0;
  FOR(i, 1, n - 1) mm += lis[i];
  if (mm <= lis[n]) printf("%lld", mm);
  else printf("%lld", mm + lis[n] >> 1);
}


J. Postman

首先很容易注意到我们无论何时均不应该在同一次投递过程中同时去原点左右两侧。因此,我们将原点两侧的信箱分开处理。对于某一侧,我们可以从远到近投递,如果某一次投递数量不足 K,那么我们可以顺便从远到近投递剩下的信箱,直到没有多余的背包空间或没有需要投递的信件方返程。复杂度 O(nlogn)

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

int n, k;
ll ans;
priority_queue lis[2];

void solve(priority_queue &q) {
  while (!q.empty()) {
    auto now = q.top(); q.pop();
    int x = now._1, m = now._2;
    ans += m / k *  ll(x) * 2;
    m -= m / k * k;
    if (!m) continue;
    ans += 2 * x;
    int rem = k - m;
    while (rem && !q.empty()) {
      auto cur = q.top(); q.pop();
      if (rem >= cur._2) {
        rem -= cur._2;
        continue;
      }
      cur._2 -= rem;
      rem = 0;
      q.push(cur);
    }
  }
}

int main() {
  scanf("%d%d", &n, &k);
  FOR(i, 1, n) {
    int x, m;
    scanf("%d%d", &x, &m);
    if (!x) continue;
    if (x < 0) lis[0].push(mp(-x, m));
    else lis[1].push(mp(x, m));
  }
  solve(lis[0]);
  solve(lis[1]);
  printf("%lld", ans);
}


C. Buggy Robot

动态规划。用 dp[x][y][pos] 表示当前在网络内点 (x, y) 且将要执行第 pos 个指令时最少修改多少次。如果 pos 位置还有指令,那么可以用当前状态免费地遵循指令更新或者花费一次修改来删除该指令。在任意时候都可以花费一次修改来让机器人移动要邻接的合法位置。可以用类似于 Dijkstra 算法的方法来更新状态。

#include 

using namespace std;
typedef long long ll;
typedef pair pii;
#define FOR(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define ROF(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define REP(i, n) FOR(i, 0, (n)-1)
#define sqr(x) ((x) * (x))
#define all(x) (x).begin(), (x).end()
#define reset(x, y) memset(x, y, sizeof(x))
#define uni(x) (x).erase(unique(all(x)), (x).end());
#define BUG(x) cerr << #x << " = " << (x) << endl
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define _1 first
#define _2 second
#define chkmin(a, b) a = min(a, b)
#define chkmax(a, b) a = max(a, b)

const int maxn = 55;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

int dp[maxn][maxn][maxn], n, m, len;
char mat[maxn][maxn], cmd[maxn];

typedef pair Node;

int main() {
  scanf("%d%d", &n, &m);
  FOR(i, 1, n) scanf("%s", mat[i] + 1);
  scanf("%s", cmd + 1);
  len = strlen(cmd + 1);
  reset(dp, 0x3f);
  int sx, sy, tx, ty;
  FOR(i, 1, n) FOR(j, 1, m) {
      if (mat[i][j] == 'R') sx = i, sy = j;
      if (mat[i][j] == 'E') tx = i, ty = j;
    }
  dp[sx][sy][1] = 0;
  set s;
  s.insert(mp(mp(0, 1), mp(sx, sy)));
  while (!s.empty()) {
    auto now = *s.begin(); s.erase(now);
    int used = now._1._1, pos = now._1._2, x = now._2._1, y = now._2._2;
    if (used > dp[x][y][pos]) continue;
    if (x == tx && y == ty) {
      printf("%d", used);
      return 0;
    }
    if (pos != len + 1) {
      int nx = x, ny = y, np = pos + 1;
      if (cmd[pos] == 'U' && nx != 1 && mat[nx - 1][ny] != '#') nx--;
      if (cmd[pos] == 'D' && nx != n && mat[nx + 1][ny] != '#') nx++;
      if (cmd[pos] == 'L' && ny != 1 && mat[nx][ny - 1] != '#') ny--;
      if (cmd[pos] == 'R' && ny != m && mat[nx][ny + 1] != '#') ny++;
      if (dp[nx][ny][np] > used) {
        dp[nx][ny][np] = used;
        s.insert(mp(mp(used, np), mp(nx, ny)));
      }
      if (dp[x][y][np] > used + 1) {
        dp[x][y][np] = used + 1;
        s.insert(mp(mp(used + 1, np), mp(x, y)));
      }
    }
    REP(i, 4) {
      int nx = x + dx[i], ny = y + dy[i];
      if (!nx) nx++;
      if (nx == n + 1) nx--;
      if (!ny) ny++;
      if (ny == m + 1) ny--;
      if (mat[nx][ny] == '#') nx -= dx[i], ny -= dy[i];
      if (dp[nx][ny][pos] > used + 1) {
        dp[nx][ny][pos] = used + 1;
        s.insert(mp(mp(used + 1, pos), mp(nx, ny)));
      }
    }
  }
}

你可能感兴趣的:(2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) 解题报告)