网络流 I

Fox Ciel is participating in a party in Prime Kingdom. There are n foxes there (include Fox Ciel). The i-th fox is ai years old.

They will have dinner around some round tables. You want to distribute foxes such that:

  1. Each fox is sitting at some table.
  2. Each table has at least 3 foxes sitting around it.
  3. The sum of ages of any two adjacent foxes around each table should be a prime number.

If k foxes f1f2, ..., fk are sitting around table in clockwise order, then for 1 ≤ i ≤ k - 1: fi and fi + 1 are adjacent, and f1 and fk are also adjacent.

If it is possible to distribute the foxes in the desired manner, find out a way to do that.

Input

The first line contains single integer n (3 ≤ n ≤ 200): the number of foxes in this party.

The second line contains n integers ai (2 ≤ ai ≤ 104).

Output

If it is impossible to do this, output "Impossible".

Otherwise, in the first line output an integer m (): the number of tables.

Then output m lines, each line should start with an integer k -=– the number of foxes around that table, and then k numbers — indices of fox sitting around that table in clockwise order.

If there are several possible arrangements, output any of them.

Examples

Input
4
3 4 8 9
Output
1
4 1 2 4 3
Input
5
2 2 2 2 2
Output
Impossible
Input
12
2 3 4 5 6 7 8 9 10 11 12 13
Output
1
12 1 2 3 6 5 12 9 8 7 10 11 4
Input
24
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Output
3
6 1 2 3 6 5 4
10 7 8 9 12 15 14 13 16 11 10
8 17 18 23 22 19 20 21 24

Note

In example 1, they can sit around one table, their ages are: 3-8-9-4, adjacent sums are: 11, 17, 13 and 7, all those integers are primes.

In example 2, it is not possible: the sum of 2+2 = 4 is not a prime number.

题目大意:

就是有n只狐狸,给了你他们的年龄,让你给他们安排位置,有几个要求,第一个是每一张桌子至少做三只狐狸,第二个是任意两只相邻位子的狐狸他们的年龄之和为一个素数。

让你输出桌子数量,然后输出每一张桌子做的人数和桌子坐了哪些人。

思路:

这个题目我一开始想到了二分图的最大匹配,但是怎么建图都感觉有点问题,然后我就看了题解,感觉题解写的挺巧妙的。

就是因为年龄一定大于等于2,所以年龄之和要是为素数就肯定是一个奇数,所以这个就把奇数和偶数分开。

奇数和偶数分开,这个就很容易想到是网络流,怎么建图我其实还是没有想清楚,继续看题解。

分开之后把奇数和源点相连,偶数和汇点相连,容量都是2.

奇数和偶数如果加和得到一个质数,那么奇数和这个偶数相连,容量为1.

为什么要这么建图呢?因为如果奇数和源点的容量应该是2,如果满流就代表着这个奇数连到了两个偶数而且奇数偶数之和为一个素数,

这个样子就找到了这个奇数左右两边相邻的数,这个样子同时满足了第一第二两个条件。

显而易见如果要满足上面的建图条件,则奇数和偶数一定要相同,因为奇数和源点容量是2,偶数和汇点容量也是2(因为一个偶数两边应该也要两个奇数,奇偶加和为一个素数)

所以这个最大流就应该==n。如果不满足就输出impossible

最后就是路径的输出了,这个用用数组来存,把连了的边存到一个数组里面,用vis进行标记来确定不会连重复的边。

然后就是一个递归来找这个桌子的数量,如果一条边它的容量为1则说明这个被经过,就可以加上这条边。

值得注意的是,这些奇数偶数肯定会形成一个环,所以不用担心可能会导致存在两个相邻的数不满足条件,

这个不确定的话可以自己模拟一下。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int INF = 0x3f3f3f3f;
struct edge
{
    int u, v, c, f;
    edge(int u, int v, int c, int f) :u(u), v(v), c(c), f(f) {}
};
vector<edge>e;
vector<int>G[maxn];
int level[maxn];//BFS分层,表示每个点的层数
int iter[maxn];//当前弧优化
int m;
void init(int n)
{
    for (int i = 0; i <= n; i++)G[i].clear();
    e.clear();
}
void add(int u, int v, int c)
{
    e.push_back(edge(u, v, c, 0));
    e.push_back(edge(v, u, 0, 0));
    m = e.size();
    G[u].push_back(m - 2);
    G[v].push_back(m - 1);
}
void BFS(int s)//预处理出level数组
//直接BFS到每个点
{
    memset(level, -1, sizeof(level));
    queue<int>q;
    level[s] = 0;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int v = 0; v < G[u].size(); v++)
        {
            edge& now = e[G[u][v]];
            if (now.c > now.f && level[now.v] < 0)
            {
                level[now.v] = level[u] + 1;
                q.push(now.v);
            }
        }
    }
}
int dfs(int u, int t, int f)//DFS寻找增广路
{
    if (u == t)return f;//已经到达源点,返回流量f
    for (int &v = iter[u]; v < G[u].size(); v++)
        //这里用iter数组表示每个点目前的弧,这是为了防止在一次寻找增广路的时候,对一些边多次遍历
        //在每次找增广路的时候,数组要清空
    {
        edge &now = e[G[u][v]];
        if (now.c - now.f > 0 && level[u] < level[now.v])
            //now.c - now.f > 0表示这条路还未满
            //level[u] < level[now.v]表示这条路是最短路,一定到达下一层,这就是Dinic算法的思想
        {
            int d = dfs(now.v, t, min(f, now.c - now.f));
            if (d > 0)
            {
                now.f += d;//正向边流量加d
                e[G[u][v] ^ 1].f -= d;
                //反向边减d,此处在存储边的时候两条反向边可以通过^操作直接找到
                return d;
            }
        }
    }
    return 0;
}
int Maxflow(int s, int t)
{
    int flow = 0;
    for (;;)
    {
        BFS(s);
        if (level[t] < 0)return flow;//残余网络中到达不了t,增广路不存在
        memset(iter, 0, sizeof(iter));//清空当前弧数组
        int f;//记录增广路的可增加的流量
        while ((f = dfs(s, t, INF)) > 0)
        {
            flow += f;
        }
    }
    return flow;
}
int p[maxn];
void init()
{
    memset(p, 0, sizeof(p));
    for (int i = 2; i < maxn; i++) p[i] = 1;
    for(ll i=2;i*i<maxn;i++)
    {
        if(p[i])
        {
            for(ll j=i*i;j<maxn;j+=i)
            {
                p[j] = 0;
            }
        }
    }
}
int a[maxn], cnt, out[maxn];
vector<int>vec[maxn];
bool vis[maxn];

void solve(int u)
{
    out[++cnt] = u;
    for(int i=0;i<vec[u].size();i++)
    {
        int v = vec[u][i];
        if (vis[v]) continue;
        vis[v] = 1;
        solve(v);
    }
}

int main()
{
    init();
    int n;
    scanf("%d", &n);
    int s = 0, t = n + 1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d", &a[i]);
        if (a[i] & 1) add(s, i, 2);
        else add(i, t, 2);
    }
    for(int i=1;i<=n;i++)
    {
        if(a[i]&1)
        {
            for(int j=1;j<=n;j++)
            {
                if (i == j) continue;
                if (p[a[i] + a[j]]) add(i, j, 1);
            }
        }
    }
    int ans = Maxflow(s, t);
    if(ans!=n)
    {
        printf("Impossible
");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        if(a[i]&1)
        {
            for(int j=0;j<G[i].size();j++)
            {
                edge now = e[G[i][j]];
                if(now.v<t&&now.v>s&&now.u<t&&now.u>s&&now.f==1)
                {
                    vec[now.u].push_back(now.v);
                    vec[now.v].push_back(now.u);
                }
            }
        }
    }
    int count = 0;
    memset(vis, 0, sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        cnt = 0;
        if(!vis[i])
        {
            count++;
            vis[i] = 1;
            solve(i);
        }
    }
    printf("%d
", count);
    memset(vis, 0, sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        cnt = 0;
        if (vis[i]) continue;
        vis[i] = 1;
        solve(i);
        
        printf("%d ", cnt);
        for (int j = 1; j < cnt; j++) printf("%d ", out[j]);
        printf("%d
", out[cnt]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/EchoZQN/p/10827819.html