網絡流 I - Fox And Dinner CodeForces - 510E
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:
- Each fox is sitting at some table.
- Each table has at least 3 foxes sitting around it.
- 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
Input4Output
3 4 8 9
1Input
4 1 2 4 3
5Output
2 2 2 2 2
Impossible
Input
12Output
2 3 4 5 6 7 8 9 10 11 12 13
1Input
12 1 2 3 6 5 12 9 8 7 10 11 4
24Output
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
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\n");
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\n", 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\n", out[cnt]);
}
return 0;
}
網絡流 I - Fox And Dinner CodeForces - 510E