Codeforces 510 E Fox And Dinner【预处理素数+建图+最大流Dinic】好题

解决FoxAndDinner竞赛编程题目,通过构建二分图并利用最大流算法分配狐狸到圆桌,确保相邻狐狸年龄之和为素数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Fox And Dinner
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

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 f1, f2, ..., 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只fox,每只fox有对应的年龄,现在要求给n只fox分桌。每个桌至少三只fox,而且相邻坐着的fox的年龄和需要是素数。

问能够分出来多少桌,每桌多少fox,然后输出每桌fox的编号。


思路:


思路来源于网络:http://www.cnblogs.com/yohaha/p/5042116.html


1、好题,首先建立一个网络(建图方法确实很有趣)。

①首先分析,我们可以将n只fox分成二分图,将奇数分成一堆,偶数分成一堆,构成二分图。其中奇数+奇数==偶数,偶数一定不是素数(抛去2)(而且已经保证了fox的年龄大于等于2.所以这里不用担心两个数加一起等于2的情况)。其中偶数+偶数==偶数,同理,那么我们能够保证每个集合中没有相交的元素相加可以构成素数。

②那么我们建立一个源点,将源点连入各个偶数节点,设定权值为2,表示其可以有两个相邻的小伙伴。

③那么我们再建立一个汇点,将各个奇数节点连入汇点,设定权值为2,表示同理。

④那么如果左边偶数数加上右边某个奇数数能够构成一个素数,那么其建一条有向边,从偶数连入奇数,设定权值为1.


2、建立好网络之后,跑一遍最大流,如果满流,说明桌位是可分的。


3、如果桌位是可分的(满流),我们对应Dfs求解即可。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[1515151];
vector<int >mp[2050];
int vis[2050];
int head[2050];
int cur[2050];
int divv[2050];
int lx[2050];
int ly[2050];
int a[205];
int Is_or[165550];//0表示素数
int n,cont,ss,tt,contx,conty;
void add(int from,int to,int w)
{
    e[cont].from=from;
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void init()
{
    memset(Is_or,0,sizeof(Is_or));
    for(int j=2;j<sqrt(165540);j++)
    {
        if(Is_or[j]==0)
        for(int k=j+j;k<=165540;k+=j)
        Is_or[k]=1;
    }
}
void getmap()
{
    cont=0;
    ss=n+1;
    tt=ss+1;
    contx=0,conty=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        if(a[i]%2==0)
        {
            add(ss,i,2);
            add(i,ss,0);
            lx[contx++]=i;
        }
        else
        {
            add(i,tt,2);
            add(tt,i,0);
            ly[conty++]=i;
        }
    }
    for(int i=0;i<contx;i++)
    {
        for(int j=0;j<conty;j++)
        {
            if(Is_or[a[lx[i]]+a[ly[j]]]==0)
            {
                add(lx[i],ly[j],1);
                add(ly[j],lx[i],0);
            }
        }
    }
}
int makedivv()
{
    queue<int >s;
    s.push(ss);
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,0x3f3f3f3f,tt);
    }
    return ans;
}
void Dfss(int u,int tot)
{
    mp[tot].push_back(u);
    vis[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v]==1)continue;
        int w=e[i].w;
        if(v!=ss&&v!=tt)
        {
            if(i%2==0&&w==0)
            {
                Dfss(v,tot);
            }
            if(i%2==1&&e[i^1].w==0)
            {
                Dfss(v,tot);
            }
        }
    }
}
int main()
{
    init();
    while(~scanf("%d",&n))
    {
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            mp[i].clear();
        }
        getmap();
        int tmp=Dinic();
        if(tmp==n)
        {
            int tot=0;
            for(int i=1;i<=n;i++)
            {
                if(vis[i]==0)
                {
                    Dfss(i,++tot);
                }
            }
            printf("%d\n",tot);
            for(int i=1;i<=tot;i++)
            {
                printf("%d",mp[i].size());
                for(int j=0;j<mp[i].size();j++)
                {
                    printf(" %d",mp[i][j]);
                }
                printf("\n");
            }
        }
        else printf("Impossible\n");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值