Codeforces Round #290 (Div. 2) E. Fox And Dinner 网络流 最大流

44 篇文章 0 订阅
10 篇文章 0 订阅
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 f1f2, ..., fk are sitting around table in clockwise order, then for 1 ≤ i ≤ k - 1fi 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.

Sample test(s)
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,终点连接所有的偶数,权值为2,奇数与偶数之和为质数,则连一条1的边,求最大流,如果最终的结果,从起点出发有权值为1的边,则说明最多只有两个人能连起来,不合题意,无解,每个奇点 偶点都有两个边相连,用dfs找出这个路径,如果大于3个连接点,则输出答案即可!这里用EK算法bfs实现求最大流!

#define INF			9000000000
#define EPS			(double)1e-9
#define mod			1000000007
#define PI			3.14159265358979
//*******************************************************************************/
#endif
#define N 205
#define M 100005
#define maxn 205
#define MOD 1000000000000000007
int n,pri[N],ansNum;
bool vis[N],prime[M];
vector<int> ans[N];
struct Edge{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct EdmondsKarp{
    int n,m;
    vector<Edge> edges;//存边 边的两倍
    vector<int> G[maxn];//邻接表,图
    int a[maxn];//起点到i的可改进量
    int p[maxn];//最短路入弧号
    void init(int n){
        FI(n) G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));//反向
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    int Maxflow(int s,int t){
        int flow = 0;
        for(;;){
            memset(a,0,sizeof(a));
            queue<int> Q;
            Q.push(s);
            a[s] = INF;
            while(!Q.empty()){
                int x = Q.front();Q.pop();
                FI(G[x].size()){
                    Edge & e = edges[G[x][i]];
                    if(!a[e.to]&&e.cap > e.flow){
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x],e.cap - e.flow);
                        Q.push(e.to);
                    }
                }
                if(a[t]) break;
            }
            if(!a[t]) break;
            for(int u = t;u !=s;u = edges[p[u]].from){
                edges[p[u]].flow += a[t];
                edges[p[u] ^ 1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
};
EdmondsKarp Ek;
void InitPrime(){
    memset(prime,true,sizeof(prime));
    prime[0] = prime[1] = false;
    for(int i=2;i<M;i++){
        if(prime[i]){
            for(int j=i+i;j<M;j+=i){
                prime[j] = false;
            }
        }
    }
}
void FindPath(int s,int isOdd,int ansi){
    vis[s] = true;
    ans[ansi].push_back(s);
    for(int i=0;i<Ek.G[s].size();i++){
            Edge e = Ek.edges[Ek.G[s][i]];
            if(e.to != (n+1) && e.to != (n)&& !vis[e.to] && (e.cap == isOdd)&& (abs(e.flow) == 1)){
                FindPath(e.to,isOdd ^ 1,ansi);
                return ;
            }
    }
}
int main()
{
    InitPrime();
    while(S(n)!=EOF)
    {
        FI(n){
            S(pri[i]);
        }
        Ek.init(n+2);
        FI(n){
            if(pri[i] & 1)
                Ek.AddEdge(n,i,2);
            else
                Ek.AddEdge(i,n+1,2);
            for(int j=0;j<n;j++){
                if( j != i && (pri[i] & 1) && prime[pri[i] + pri[j]])
                    Ek.AddEdge(i,j,1);
            }
        }
        Ek.Maxflow(n,n+1);
        bool flag = true;
        for(int i=0;i<Ek.G[n].size() && flag;i++){
            Edge e = Ek.edges[Ek.G[n][i]];
            if(e.cap && abs(e.flow) != 2){
                flag = false;
            }
        }
        for(int i=0;i<Ek.G[n+1].size() && flag;i++){
            Edge e = Ek.edges[Ek.G[n+1][i]];
            if(!e.cap && abs(e.flow) != 2){
                flag = false;
            }
        }
        if(flag){
            ansNum = 0;
            memset(vis,false,sizeof(vis));
            for(int i=0;i<Ek.G[n].size();i++){
                Edge e = Ek.edges[Ek.G[n][i]];
                if(!vis[e.to]){
                    ans[ansNum].clear();
                    FindPath(e.to,1,ansNum);
                    if(ans[ansNum].size()<=2){
                        ansNum = 0;
                        break;
                    }
                    ansNum++;
                }
            }
            if(ansNum == 0){
                printf("Impossible\n");
                continue;
            }
            printf("%d\n",ansNum);
            FI(ansNum){
                printf("%d",ans[i].size());
                FJ(ans[i].size()){
                    printf(" %d",ans[i][j]+1);
                }
                printf("\n");
            }
        }
        else {
            printf("Impossible\n");
        }
    }
    return 0;
}

Dinic 算法实现最大流
#define INF			9000000000
#define EPS			(double)1e-9
#define mod			1000000007
#define PI			3.14159265358979
//*******************************************************************************/
#endif
#define N 205
#define M 100005
#define maxn 205
#define MOD 1000000000000000007
int n,pri[N],ansNum;
bool vis[N],prime[M];
vector<int> ans[N];
struct Edge{
    int from,to,cap,flow;
    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct EdmondsKarp{
    int n,m;
    vector<Edge> edges;//存边 边的两倍
    vector<int> G[maxn];//邻接表,图
    int a[maxn];//起点到i的可改进量
    int p[maxn];//最短路入弧号
    void init(int n){
        FI(n) G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));//反向
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    int Maxflow(int s,int t){
        int flow = 0;
        for(;;){
            memset(a,0,sizeof(a));
            queue<int> Q;
            Q.push(s);
            a[s] = INF;
            while(!Q.empty()){
                int x = Q.front();Q.pop();
                FI(G[x].size()){
                    Edge & e = edges[G[x][i]];
                    if(!a[e.to]&&e.cap > e.flow){
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x],e.cap - e.flow);
                        Q.push(e.to);
                    }
                }
                if(a[t]) break;
            }
            if(!a[t]) break;
            for(int u = t;u !=s;u = edges[p[u]].from){
                edges[p[u]].flow += a[t];
                edges[p[u] ^ 1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
};
struct Dinic{
    int n,m,s,t;
    vector<Edge> edges;//存边 边的两倍
    vector<int> G[maxn];//邻接表,图
    bool vis[maxn];//BFS使用
    int d[maxn];//起点到i的距离
    int cur[maxn];//当前弧下标
    void init(int n){
        FI(n) G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,int cap){
        edges.push_back(Edge(from,to,cap,0));
        edges.push_back(Edge(to,from,0,0));//反向
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool BFS(){
        memset(vis,0,sizeof(vis));
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!Q.empty()){
            int x = Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge & e  = edges[G[x][i]];
                if(!vis[e.to] && e.cap > e.flow){
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a){
        if(x == t || a== 0) return a;
        int flow = 0,f;
        for(int  i= cur[x];i<G[x].size();i++){
            Edge & e = edges[G[x][i]];
            if(d[x] + 1 == d[e.to] && ( f= DFS(e.to,min(a,e.cap - e.flow)))>0){
                e.flow += f;
                edges[G[x][i]^1].flow -= f;
                flow += f;
                a -= f;
                if( a== 0)break;
            }
        }
        return flow;
    }
    int Maxflow(int s,int t){
        this->s = s;this-> t = t;
        int flow = 0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow += DFS(s,INF);
        }
        return flow;
    }
};
//EdmondsKarp Ek;
Dinic Ek;
void InitPrime(){
    memset(prime,true,sizeof(prime));
    prime[0] = prime[1] = false;
    for(int i=2;i<M;i++){
        if(prime[i]){
            for(int j=i+i;j<M;j+=i){
                prime[j] = false;
            }
        }
    }
}
void FindPath(int s,int isOdd,int ansi){
    vis[s] = true;
    ans[ansi].push_back(s);
    for(int i=0;i<Ek.G[s].size();i++){
            Edge e = Ek.edges[Ek.G[s][i]];
            if(e.to != (n+1) && e.to != (n)&& !vis[e.to] && (e.cap == isOdd)&& (abs(e.flow) == 1)){
                FindPath(e.to,isOdd ^ 1,ansi);
                return ;
            }
    }
}
int main()
{
    InitPrime();
    while(S(n)!=EOF)
    {
        FI(n){
            S(pri[i]);
        }
        Ek.init(n+2);
        FI(n){
            if(pri[i] & 1)
                Ek.AddEdge(n,i,2);
            else
                Ek.AddEdge(i,n+1,2);
            for(int j=0;j<n;j++){
                if( j != i && (pri[i] & 1) && prime[pri[i] + pri[j]])
                    Ek.AddEdge(i,j,1);
            }
        }
        Ek.Maxflow(n,n+1);
        bool flag = true;
        for(int i=0;i<Ek.G[n].size() && flag;i++){
            Edge e = Ek.edges[Ek.G[n][i]];
            if(e.cap && abs(e.flow) != 2){
                flag = false;
            }
        }
        for(int i=0;i<Ek.G[n+1].size() && flag;i++){
            Edge e = Ek.edges[Ek.G[n+1][i]];
            if(!e.cap && abs(e.flow) != 2){
                flag = false;
            }
        }
        if(flag){
            ansNum = 0;
            memset(vis,false,sizeof(vis));
            for(int i=0;i<Ek.G[n].size();i++){
                Edge e = Ek.edges[Ek.G[n][i]];
                if(!vis[e.to]){
                    ans[ansNum].clear();
                    FindPath(e.to,1,ansNum);
                    if(ans[ansNum].size()<=2){
                        ansNum = 0;
                        break;
                    }
                    ansNum++;
                }
            }
            if(ansNum == 0){
                printf("Impossible\n");
                continue;
            }
            printf("%d\n",ansNum);
            FI(ansNum){
                printf("%d",ans[i].size());
                FJ(ans[i].size()){
                    printf(" %d",ans[i][j]+1);
                }
                printf("\n");
            }
        }
        else {
            printf("Impossible\n");
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值