Repairing Company

题目描述

Lily runs a repairing company that services the $Q$ blocks in the city. One day the company receives $M$ repair tasks, the $i$-th of which occurs in block $p_i$, has a deadline $t_i$ on any repairman’s arrival, which is also its starting time, and takes a single repairman $d_i$ time to finish. Repairmen work alone on all tasks and must finish one task before moving on to another. With a map of the city in hand, Lily want to know the minimum number of repairmen that have to be assign to this day’s tasks.

题意概述

有$Q$个街道和$M$个任务。给出两两街道之间的距离以及每个任务的地点$p_i$、开始时间$t_i$和花费时间$d_i$。问至少需要多少个工人才能完成所有任务。工人开始时可以在任意街道,一个工人只能同时做一个任务。
数据范围:$1 \le Q \le 20, \; 1 \le M \le 200$。

算法分析

首先用Floyd求出任意两点之间的距离,用$dist_{i, j}$表示。对于两个任务$i, j$,若$t_i+d_i+dist_{p_i, p_j} \le t_j$,那么做第$i$个任务的工人可以继续做第$j$个任务。显然,任务之间构成了一张有向无环图,问题转化为最小覆盖问题。将图中每个点$u$拆成$u_0, u_1$两个点。若原图中有一条边$(u, v)$,则在新图中连边$(u_0, v_1)$。答案就是$M$减去新图的最大匹配数。

代码

#include <cstdio>
#include <cstring>

int min(int a, int b) {
  return a < b ? a : b;
}

static const int Q = 25;
static const int M = 405;
int mp[Q][Q], p[M], t[M], d[M];
int nume, h[M];
struct Edge {
  int v, f, nxt;
} e[M * M << 1];

void add_edge(int u, int v, int f) {
  e[++ nume] = (Edge) { v, f, h[u] }, h[u] = nume;
  e[++ nume] = (Edge) { u, 0, h[v] }, h[v] = nume;
}

namespace flow {
  int S, T, g[M], dist[M], que[M], gap[M];

bool bfs() {
    memcpy(g, h, sizeof h);
    memset(dist, 0x3f, sizeof dist), memset(gap, 0, sizeof gap);
    int qb = 0, qe = 1; dist[T] = 0, que[0] = T, ++ gap[0];
    while (qb < qe) {
      int u = que[qb ++];
      for (int i = g[u]; i; i = e[i].nxt)
        if (e[i ^ 1].f && dist[e[i].v] > dist[u] + 1)
          dist[e[i].v] = dist[u] + 1, que[qe ++] = e[i].v, ++ gap[dist[e[i].v]];
    }
    return dist[S] < 1e9;
  }

int dfs(int t, int low) {
    if (t == T) return low;
    int used = 0;
    for (int &i = g[t]; i; i = e[i].nxt)
      if (e[i].f && dist[e[i].v] == dist[t] - 1) {
        int flow = dfs(e[i].v, min(e[i].f, low - used));
        if (flow) e[i].f -= flow, e[i ^ 1].f += flow, used += flow;
        if (used == low || dist[S] > T) return used;
      }
    if (! -- gap[dist[t]]) dist[S] = T + 1;
    else ++ gap[++ dist[t]], g[t] = h[t];
    return used;
  }

int maxflow() {
    bfs(); int flow = 0;
    while (dist[S] <= T) flow += dfs(S, 1e9);
    return flow;
  }
}

int main() {
  int q, m;
  while (scanf("%d%d", &q, &m), q) {
    nume = 1, memset(h, 0, sizeof h);
    flow :: S = m << 1, flow :: T = (m << 1) + 1;
    for (int i = 0; i < q; ++ i)
      for (int j = 0; j < q; ++ j) scanf("%d", &mp[i][j]);
    for (int i = 0; i < q; ++ i)
      for (int j = 0; j < q; ++ j) if (! ~ mp[i][j]) mp[i][j] = 1e9;
    for (int k = 0; k < q; ++ k)
      for (int i = 0; i < q; ++ i)
        for (int j = 0; j < q; ++ j) mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
    for (int i = 0; i < m; ++ i) scanf("%d%d%d", &p[i], &t[i], &d[i]), -- p[i];
    for (int i = 0; i < m; ++ i)
      for (int j = 0; j < m; ++ j) if (i != j && t[i] + d[i] + mp[p[i]][p[j]] <= t[j]) add_edge(i, j + m, 1);
    for (int i = 0; i < m; ++ i) add_edge(flow :: S, i, 1), add_edge(i + m, flow :: T, 1);
    printf("%d\n", m - flow :: maxflow());
  }
  return 0;
}

Fox and Dinner

题目描述

Fox Ciel is participating in a party in Prime Kingdom. There are $n$ foxes there (include Fox Ciel). The $i$-th fox is $a_i$ years old.
They will have dinner around some round tables. You want to distribute foxes such that:

  • each fox is sitting at some table,
  • each table has at least 3 foxes sitting around it,
  • the sum of ages of any two adjacent foxes around each table should be a prime number.

If $k$ foxes $f_1, f_2, \ldots, f_k$ are sitting around table in clockwise order, then for $1 \le i \le k-1$: $f_i$ and $f_{i+1}$ are adjacent, and $f_1$ and $f_k$ are also adjacent.
If it is possible to distribute the foxes in the desired manner, find out a way to do that.

题意概述

给定$n$个数$a_i$,你需要将它们分成若干个圈,使得每个圈中至少有$3$个数,且任意两个相邻的数的和为质数。
数据范围:$3 \le n \le 200, \; 2 \le a_i \le 10000$。

算法分析

因为每个数都大于$1$,所以任意两个相邻的数必定是一奇一偶。对于任意一个奇数$a_i$和任意一个偶数$a_j$,如果$a_i+a_j$是质数,那么就由$a_i$向$a_j$连一条流量为$1$的边。由于每个数都必须与另外两个数相连,因此可以由一个假设的源点向每个奇数的点连一条流量为$2$的边,由每个偶数的点向一个假设的汇点连一条流量为$2$的边。找出这张图的最大流,如果源点连出的边和连向汇点的边全部满流则说明有解。最后遍历找出所有环即可。

代码

#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
struct edge {
    int v, f, nxt;
} e[50001];
int n, nume = 1, top, src, sink, a[202], h[202], g[202], dist[202], que[202];
bool vis[202];
vector<int> ans[201];
void add_edge(int u, int v, int f) {
    e[++nume].v = v, e[nume].f = f, e[nume].nxt = h[u], h[u] = nume;
    e[++nume].v = u, e[nume].f = 0, e[nume].nxt = h[v], h[v] = nume;
}
void bfs() {
    memset(dist, 0, sizeof(dist));
    que[1] = src, dist[src] = 1;
    int s = 0, t = 1;
    while (s < t) {
        int u = que[++s];
        for (int i = h[u]; i; i = e[i].nxt) {
            if (e[i].f && !dist[e[i].v]) {
                que[++t] = e[i].v, dist[e[i].v] = dist[u] + 1;
            }
        }
    }
}
int dfs(int u, int delta) {
    if (u == sink) return delta;
    int ret = 0;
    for (int i = g[u]; i; i = e[i].nxt) {
        if (e[i].f && dist[e[i].v] == dist[u] + 1) {
            int dd = dfs(e[i].v, min(e[i].f, delta));
            e[i].f -= dd, e[i ^ 1].f += dd;
            if (e[i].f) g[u] = i;
            delta -= dd, ret += dd;
        }
    }
    return ret;
}
int max_flow() {
    int ret = 0;
    while (1) {
        bfs();
        if (!dist[sink]) return ret;
        for (int i = 0; i <= n + 1; ++i) g[i] = h[i];
        ret += dfs(src, 1e9);
    }
}
bool is_prime(int t) {
    int k = (int) sqrt(t);
    for (int i = 2; i <= k; ++i) if (!(t % i)) return false;
    return true;
}
void find(int u, int t) {
    vis[u] = true, ans[t].push_back(u);
    for (int i = h[u]; i; i = e[i].nxt) {
        if (e[i].v && e[i].v != n + 1 && e[i].f == !(a[u] & 1) && !vis[e[i].v]) find(e[i].v, t);
    }
}
int main()
{
    cin >> n;
    src = 0, sink = n + 1;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (a[i] & 1) add_edge(src, i, 2);
        else add_edge(i, sink, 2);
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (a[i] & 1 && !(a[j] & 1) && is_prime(a[i] + a[j])) add_edge(i, j, 1);
        }
    }
    max_flow();
    for (int i = h[0]; i; i = e[i].nxt) {
        if (e[i].f) {
            cout << "Impossible" << endl;
            return 0;
        }
    }
    for (int i = h[n + 1]; i; i = e[i].nxt) {
        if (e[i].f < 2) {
            cout << "Impossible" << endl;
            return 0;
        }
    }
    for (int i = 1; i <= n; ++i) if (!vis[i]) top++, find(i, top);
    cout << top << endl;
    for (int i = 1; i <= top; ++i) {
        cout << ans[i].size() << ' ';
        for (vector<int>::iterator iter = ans[i].begin(); iter != ans[i].end(); ++iter) {
            cout << *iter << ' ';
        }
        cout << endl;
    }
    return 0;
}