Codeforces Round #290 Div. 1 C. Fox And Dinner 奇偶建图跑最大流
传送门: https://codeforces.com/contest/512/problem/C
题意
有 n 个 数 , 问 能 不 能 把 n 个 数 分 成 k 桌 , 且 要 求 如 下 有n个数,问能不能把n个数分成k桌,且要求如下 有n个数,问能不能把n个数分成k桌,且要求如下
- 每 桌 至 少 3 个 数 以 上 每桌至少3个数以上 每桌至少3个数以上
- 一 桌 上 相 邻 的 数 相 加 为 素 数 一桌上相邻的数相加为素数 一桌上相邻的数相加为素数
问 能 不 能 构 造 出 k 桌 , 能 则 输 出 , 否 则 输 出 − 1. 问能不能构造出k桌,能则输出,否则输出-1. 问能不能构造出k桌,能则输出,否则输出−1.
思路
题 目 要 求 相 连 的 数 相 加 为 素 数 , 我 们 知 道 , 这 两 个 数 一 定 是 一 奇 一 偶 。 题目要求相连的数相加为素数,我们知道,这两个数一定是一奇一偶。 题目要求相连的数相加为素数,我们知道,这两个数一定是一奇一偶。
而 奇 数 相 邻 两 个 一 定 是 偶 数 , 偶 数 相 邻 两 个 一 定 是 奇 数 。 所 以 可 以 建 立 网 络 流 模 型 。 而奇数相邻两个一定是偶数,偶数相邻两个一定是奇数。所以可以建立网络流模型。 而奇数相邻两个一定是偶数,偶数相邻两个一定是奇数。所以可以建立网络流模型。
先 判 断 两 个 数 相 加 是 否 为 素 数 , 是 的 话 就 将 奇 数 向 偶 数 连 一 条 有 向 边 , 并 且 容 量 为 1 。 先判断两个数相加是否为素数,是的话就将奇数向偶数连一条有向边,并且容量为1。 先判断两个数相加是否为素数,是的话就将奇数向偶数连一条有向边,并且容量为1。
而 这 个 奇 数 可 以 连 接 两 个 偶 数 , 所 以 将 源 点 向 这 个 奇 数 连 接 一 条 有 向 边 , 容 量 为 2 。 而这个奇数可以连接两个偶数,所以将源点向这个奇数连接一条有向边,容量为2。 而这个奇数可以连接两个偶数,所以将源点向这个奇数连接一条有向边,容量为2。
同 样 , 每 个 偶 数 都 要 向 汇 点 连 接 一 条 有 向 边 , 容 量 为 2 。 同样,每个偶数都要向汇点连接一条有向边,容量为2。 同样,每个偶数都要向汇点连接一条有向边,容量为2。
跑 一 遍 最 大 流 , 如 果 最 大 流 = n , 则 是 可 以 得 到 k 桌 。 在 残 留 网 络 上 跑 一 遍 D F S 即 可 。 跑一遍最大流,如果最大流=n,则是可以得到k桌。在残留网络上跑一遍DFS即可。 跑一遍最大流,如果最大流=n,则是可以得到k桌。在残留网络上跑一遍DFS即可。
注 意 : n 为 奇 数 一 定 是 不 存 在 。 注意:n为奇数一定是不存在。 注意:n为奇数一定是不存在。
Code(30MS)
#include "bits/stdc++.h"
using namespace std;
#define INF 0x3f3f3f3f
const int N = 405, M = 10005;
int n, m, s, t;
int maxflow;
int deep[N], cur[N];
struct Edge {
int v, next, cap;
}e[M << 1];
int head[M << 1], cnt;
void init() {
mem(head, -1);
cnt = maxflow = 0;
}
inline void add(int u, int v, int cap) {
e[cnt].v = v;
e[cnt].cap = cap;
e[cnt].next = head[u];
head[u] = cnt++;
e[cnt].v = u;
e[cnt].cap = 0;
e[cnt].next = head[v];
head[v] = cnt++;
}
bool bfs() {
for(int i = 0;i <= t; i++) {
deep[i] = -1; cur[i] = head[i];
}
queue<int> q;
q.push(s); deep[s] = 0;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(deep[v] == -1 && e[i].cap) {
deep[v] = deep[u] + 1;
q.push(v);
}
}
}
if(deep[t] >= 0) return true;
else return false;
}
int dfs(int u, int mx) {
int a;
if(u == t) return mx;
for(int i = cur[u]; ~i; i = e[i].next) {
cur[u] = i;
int v = e[i].v;
if(e[i].cap && deep[v] == deep[u] + 1 && (a = dfs(v, min(mx, e[i].cap)))) {
e[i].cap -= a;
e[i ^ 1].cap += a;
return a;
}
}
return 0;
}
void dinic() {
int res;
while(bfs()) {
while(1) {
res = dfs(s, INF);
if(!res) break;
maxflow += res;
}
}
}
const int maxn = 4e4 + 10;
bool is_prime[maxn];
void sieve() {
for(int i = 2;i < maxn; i++) {
if(!is_prime[i]) {
for(int j = i + i;j < maxn; j += i) {
is_prime[j] = true;
}
}
}
}
vector<int> ans[N];
bool vis[N];
int ver;
void DFS(int u) {
vis[u] = 1;
ans[ver].push_back(u); int v;
for(int i = head[u]; ~i; i = e[i].next) {
if(e[i].cap) continue;
v = e[i].v;
if(vis[v]) continue;
vis[v] = 1;
ans[ver].push_back(v);
break;
}
for(int i = head[v]; ~i; i = e[i].next) {
if(e[i ^ 1].cap) continue;
v = e[i].v;
if(vis[v]) continue;
vis[v] = 1;
DFS(v);
}
}
vector<int> a(405);
void solve() {
sieve();
init();
cin >> n;
if(n & 1) {
cout << "Impossible" << endl;
return ;
}
s = 0; t = n + 1;
for(int i = 1;i <= n; i++) {
cin >> a[i];
if(a[i] & 1) add(s, i, 2); // 源点连接奇数
else add(i, t, 2); // 偶数连接汇点
}
for(int i = 1;i <= n; i++) {
for(int j = 1;j <= n; j++) {
if(!is_prime[a[i] + a[j]]) {
if(a[i] & 1)
add(i, j, 1); // 奇数连接偶数
}
}
}
dinic();
if(maxflow != n) {
cout << "Impossible" << endl;
return ;
}
for(int i = 1;i <= n; i++) {
if(!vis[i] && (a[i] & 1)) {
DFS(i); // 搜索每一桌
ver++;
}
}
cout << ver << endl;
for(int i = 0;i < ver; i++) {
cout << ans[i].size();
for(int j = 0;j < ans[i].size(); j++) {
cout << " " << ans[i][j];
}
cout << endl;
}
}
signed main() {
solve();
}