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();
}