题目链接:点击打开链接
题意:
给定n个人和其权值。
让人坐到任意多张圆形的桌子上使得任意两个相邻的人权值和为素数,且一张桌子至少3个人
输出桌子数和每张桌子坐的人(输出任意一个具体方案)
思路:
首先人的权值都是>=2的,也是就说组合成的素数一定是奇数
奇数 = 奇数+偶数。所以桌子上的人一定是奇偶交错的,而且一张桌子人数是偶数个(即n必须为偶数,一半是奇数,一半是偶数)
由上得:一个奇数要选择2个偶数,一个偶数要被2个奇数选择。
所以得到一个二部图,X集为奇数,Y集为偶数
网络流:
奇数连源点,flow=2
偶数连汇点,flow=2
if(奇数+偶数 == Prime)
奇数向偶数连一条flow=1的边
满流时就说明有解
。
输出方案时把连通块放一桌即可。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
#define inf 1073741824
#define N 500
#define M 100100
//N为点数 M为边数
struct Edge{
int from, to, cap, nex;
}edge[M * 2];//双向边,注意RE 注意这个模版是 相同起末点的边 同时有效而不是去重
int head[N], tot;//2个要初始化-1和0
void add(int u, int v, int cap, int rw = 0){//网络流要加反向弧,即u->v 为10 则 v->u为 -10
Edge E = { u, v, cap, head[u] };
edge[tot] = E;
head[u] = tot++;
Edge E2 = { v, u, rw, head[v] }; //如果是无向边则rw的参数值和cap相同(即 add(u,v,cap,cap) ),若是有向边则rw不写(即 add(u,v,cap); )
edge[tot] = E2;
head[v] = tot++;
}
int dis[N], cur[N];//dis[i]表示i点距离起点的距离 cur[i]表示i点所连接的边中 正在考虑的边 优化不再考虑已经用过的点 初始化为head
bool vis[N];
bool BFS(int Start, int End){//跑一遍最短路
memset(vis, 0, sizeof(vis));
memset(dis, -1, sizeof(dis));
queue<int>Q;
Q.push(Start); dis[Start] = 0; vis[Start] = 1;
while (!Q.empty())
{
int u = Q.front(); Q.pop();
for (int i = head[u]; i != -1; i = edge[i].nex){
Edge E = edge[i];
if (!vis[E.to] && E.cap > 0)
{
vis[E.to] = true;
dis[E.to] = dis[u] + 1;
if (E.to == End) return true;
Q.push(E.to);
}
}
}
return false;
}
int DFS(int x, int a, int End){//当前 流入x 的流量是a 流量a 是所有流过边中 边权的最小值
if (x == End || a == 0)return a;
int flow = 0, f; //flow表示从x点流到下面所有点,最大的流量
for (int& i = cur[x]; i != -1; i = edge[i].nex)
{
Edge& E = edge[i];
if (dis[x] + 1 == dis[E.to] && (f = DFS(E.to, min(a, E.cap), End))>0)
{
E.cap -= f;
edge[i ^ 1].cap += f;//反向边要减掉
flow += f;
a -= f;
if (a == 0)break;
}
}
return flow;
}
int Dinic(int Start, int End){
int flow = 0;
while (BFS(Start, End)){ //当存在源点到汇点的路径时
memcpy(cur, head, sizeof(head));//把head的数组复制过去
flow += DFS(Start, inf, End);
}
return flow;
}
void init(){ memset(head, -1, sizeof head); tot = 0; }
int prime[10000], primenum;//有primenum个素数 math.h
set<int>p;
void PRIME(int Max_Prime){
p.clear();
primenum = 0;
prime[primenum++] = 2;
for (int i = 3; i <= Max_Prime; i += 2)
for (int j = 0; j<primenum; j++)
if (i%prime[j] == 0)break;
else if (prime[j]>sqrt((double)i) || j == primenum - 1)
{
prime[primenum++] = i;
break;
}
for (int i = 0; i < primenum; i++)p.insert(prime[i]);
}
int a[N], n;
vector<int>G[N];
int top;
bool Vis[N];
void dfs(int u, vector<int>&x){
Vis[u] = 1;
x.push_back(u);
for (int i = head[u]; ~i; i = edge[i].nex){
int v = edge[i].to; if (Vis[v])continue;
if (a[u] & 1){
if (edge[i].cap != 1){
edge[i].cap++;
dfs(v, x);
}
}
else {
if (edge[i].cap){
edge[i].cap--;
dfs(v, x);
}
}
}
}
int main(){
PRIME(20000);
while (cin >> n){
for (int i = 0; i < n; i++)cin >> a[i];
if (n & 1){ puts("Impossible"); continue; }
init();
int from = n, to = n + 1;
Vis[from] = Vis[to] = 1;
for (int i = 0; i < n; i++){
G[i].clear();
Vis[i] = 0;
if (a[i] & 1)
add(from, i, 2);
else
add(i, to, 2);
for (int j = 0 ; j < n; j++)
if (p.count(a[i] + a[j]))
{
if (a[i] & 1)
add(i, j, 1);
}
}
int all = Dinic(from, to);
if (all == n){
top = 0;
for (int i = 0; i < n; i++)
if (Vis[i] == 0){
dfs(i, G[top++]);
}
printf("%d\n", top);
for (int i = 0; i < top; i++)
{
printf("%d ", G[i].size());
for (int j = 0; j < G[i].size(); j++)printf("%d ", G[i][j]+1); puts("");
}
}
else puts("Impossible");
}
return 0;
}