版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tawn0000/article/details/83218185
P2765 魔术球问题
题目描述
«问题描述:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
«编程任务:
对于给定的n,计算在n根柱子上最多能放多少个球。
输入输出格式
输入格式:
第1 行有1个正整数n,表示柱子数。
输出格式:
程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。
输入输出样例
输入样例#1: 复制
4
输出样例#1: 复制
11 1 8 2 7 9 3 6 10 4 5 11
说明
感谢 @PhoenixEclipse 提供spj
4<=n<=55
从一个气球开始跑,对于加入每个气球,拆成两个点分别连到s,t, 对于 j< 当前标号p, 对于j+p是完全平方数的建边 j-p+n;
这样建完图以后,抽象化问题为求最少多少柱子能把p个气球放满,实际上求当前图的最小路径覆盖,不知道什么是最小路径覆盖,点击上面。 最小路径覆盖 = 总数 - 二分图最大匹配数 , 最大匹配数可以用最大流求得,然后用nxt 保存匹配方案。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 10000 + 10;
const int maxm = 1e6 + 10;
int n,m;
int l[maxn];//记录层数
int h[maxn];//链式前向星
int cur[maxn];
int tot = 0;
int nxt[maxn];
int p = 0;
int vis[maxn];
struct edge
{
int to;
int c;
int next;
edge(int x = 0, int y = 0, int z = 0) : to(x), c(y), next(z) {}
}es[maxm*2];//记录边 注意是2倍
void add_edge(int u, int v, int c)
{
es[tot] = edge(v,c,h[u]);
h[u] = tot++;
es[tot] = edge(u,0,h[v]);
h[v] = tot++;
// cout << u <<" " <<v <<endl;
}
bool bfs(int s, int t)
{
memset(l,0,sizeof(l));
l[s] = 1;
queue <int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
//cout << u <<endl;
q.pop();
if(u == t) return true;
for(int i = h[u]; i != -1; i = es[i].next)
{
int v = es[i].to;
if(!l[v] && es[i].c) {l[v] = l[u] + 1; q.push(v);}
}
}
return false;
}
int dfs(int x, int t, int mf)
{
if(x == t) return mf;
int ret = 0;
for(int &i = cur[x]; i != -1; i = es[i].next)
{
if(es[i].c && l[x] == l[es[i].to] - 1)
{
int f = dfs(es[i].to,t,min(es[i].c,mf - ret));
if(f) nxt[x] = es[i].to;
es[i].c -= f;
es[i^1].c += f;
ret += f;
if(ret == mf) return ret;
}
}
return ret;
}
int dinic(int s, int t)
{
int ans = 0;
while(bfs(s,t))
{
for(int i = 0; i <= t; i++) cur[i] = h[i];
ans += dfs(s,t,INF);
}
return ans;
}
bool isp(int x)
{
int y = sqrt(x);
return y*y == x;
}
int q[maxn];
int main()
{
int ans = 0,res=0;
tot = 0;
scanf("%d",&n);
memset(nxt,-1,sizeof(nxt));
memset(h,-1,sizeof(h));
int s = 0,t = 4000+1;
m = 2000;
while(++p)
{
add_edge(s,p,1);
add_edge(p+m,t,1);
for(int i = 1; i < p; i++)
if(isp(i+p)) add_edge(i,p+m,1);
res += dinic(s,t);
if(p-res <= n) ans = p;
else break;
}
tot = 0;
memset(nxt,-1,sizeof(nxt));
memset(h,-1,sizeof(h));
for(int i = 1; i <= ans; i++)
{
add_edge(s,i,1);
add_edge(i+m,t,1);
for(int j = i+1; j <= ans; j++)
if(isp(i+j)) add_edge(i,j+m,1);
}
dinic(s,t);
printf("%d\n",ans);
memset(vis,false,sizeof(vis));
// for(int i = 1; i <= n; i++)
// cout << nxt[i]-m<< " ";
//cout <<endl;
for(int i = 1; i <= ans; i++)
{
if(!vis[i])
{
vector <int> v;
int t = i;
do{
v.push_back(t);
t = nxt[t]-m;
}while(t > 0);
for(int j = 0; j < v.size(); j++)
{
vis[v[j]] = true;
printf("%d%c",v[j], j == v.size() -1 ? '\n' : ' ');
}
}
}
return 0;
}