网络流24题(四)
网络流24题(4)
四、魔术球问题
题目描述
假设有 \(n\) 根柱子,现要按下述规则在这 \(n\) 根柱子中依次放入编号为 \(1\),\(2\),\(3\),...的球“
-
每次只能在某根柱子的最上面放球。
-
同一根柱子中,任何 \(2\) 个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 \(n\) 根柱子上最多能放多少个球。例如,在 \(4\) 根柱子上最多可放 \(11\) 个球。
对于给定的 \(n\),计算在 \(n\) 根柱子上最多能放多少个球。
输入格式
只有一行一个整数 \(n\),代表柱子数。
输出格式
本题存在 Special Judge。
请将 \(n\) 根柱子上最多能放的球数以及相应的放置方案输出。
输出的第一行是球数。
接下来的 \(n\) 行,每行若干个整数,代表一根柱子上的球的编号,数字间用单个空格隔开。
题解
模型:
如果有\(k\)个球,那么建图,转换为\(DAG\)最小覆盖。
建图与实现:
我们可以这么建一张图:
对每一对\((i,j)\),如果\(i+j\)是一个平方数,那么我们就让\(i\)与\(j\)之间连一条有向边,方向为小的指向大的,不难发现这是一张\(DAG\)图。
手玩之后发现,对于\(n\)个柱子的球的个数\(k\)公式为\(k =floor(\frac{n^2}{2})-1\)。
(关于这个结论可以看这个证明,并且在这里面有一种贪心写法)
于是参照之前的写法写即可。
我的网络流方案于网上的不太相同,我的写法大概快了十倍。
网上的写法大概是不断的增加球,并对其不断地求最小路径覆盖,对于每一个路径覆盖的值都找最多的球数。更详细的方法可以找其他的资料查看。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll N = 3500,M = 2e5+5,inf = 0x3f3f3f3f;
ll head[N],cnt = 1;
struct Edge{ll to,w,nxt;}edge[M];
void add(ll u,ll v,ll w){
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
ll n,m,s,t,lv[N],cur[N];
bool bfs(){
memset(lv, -1, sizeof(lv));
lv[s] = 0;
memcpy(cur, head, sizeof(head));
queue<ll> q;q.push(s);
while (!q.empty()){
int p = q.front();q.pop();
for (ll eg = head[p]; eg; eg = edge[eg].nxt){
ll to = edge[eg].to, vol = edge[eg].w;
if (vol > 0 && lv[to] == -1)lv[to] = lv[p] + 1, q.push(to);
}
}
return lv[t] != -1;
}
ll dfs(ll p = s, ll flow = inf){
if (p == t)return flow;
int rmn = flow;
for (ll &eg = cur[p]; eg; eg = edge[eg].nxt){
if (!rmn)break;
int to = edge[eg].to, vol = edge[eg].w;
if (vol > 0 && lv[to] == lv[p] + 1){
ll c = dfs(to, min(vol, rmn));
rmn -= c;
edge[eg].w -= c;
edge[eg ^ 1].w += c;
}
}
return flow - rmn;
}
ll dinic(){
ll ans = 0;
while(bfs()) ans += dfs();
return ans;
}
ll f[N],vis[N];
void print(){
memset(f,-1,sizeof f);
for(ll eg = head[0];eg;eg = edge[eg].nxt){
if(edge[eg].w == 0){
ll v = edge[eg].to;
for(ll j = head[v];j;j = edge[j].nxt){
if(edge[j].w == 0){
f[v] = edge[j].to-n;
vis[edge[j].to-n] = true;
}
}
}
}
for(ll i = 1;i <= n;i++){
if(!vis[i]){
ll x = i;
cout<<x;
while(f[x]!=-1){
cout<<' '<<f[x];
x = f[x];
}
cout<<endl;
}
}
}
bool check(ll x){
ll y = sqrt(x);
return x == y*y;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;n++;
ll k = floor(n*n/2)-1;
n = k;
cout<<n<<endl;
for(ll i = 1;i <= n;i++) {
add(0,i,1);add(i,0,0);
add(i+n,2*n+1,1);add(2*n+1,i+n,0);
}
for(ll i = 1;i <= n;i++){
for(ll j = i+1;j <= n;j++){
if(check(i+j)){
add(i,j+n,1);
add(j+n,i,0);
}
}
}
s = 0,t = 2*n+1;
ll ss = dinic();
print();
return 0;
}