题目大意:

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。

关键字:网络流 拆点 上下界 二分查找

网络流:

想象拿柱子去串球,一个柱子就相当于一个流量为1的流。

拆点建图:

为保证每个球都能串在一个柱子上,将每个球拆成一条边,要求边内流量必须为1(即该边上下界都为1)。

若两球数字和为完全平方数,则将一球边的to节点连另一球边的from节点(注意球是按顺序放的,因此若a数字小于b数字,要么a连b,要么b连a,不能双向都连)。

为了满足网络流形式,“s”点向每个球边from点连边,每个球边to点向t点连边,容量为1。

因为柱子数量有限,因此将s点向“s”点连容量为柱子数量的边。

上下界:

根据上下界网络流处理办法,S点连每个球边to节点,每个球边from节点连T节点。之前的球边容量变为0,删去。t点向s点连容量∞边,即t,s点合并(合并点在代码中为orgT,"s"点为orgS)。

枚举:二分枚举球的数量直到得出答案。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cassert>
using namespace std; //#define test
#define INF 0x3f3f3f3f
#define P(x) x*2+3
#define Q(x) x*2+4
#define arcP(x) (x-3)/2
#define arcQ(x) (x-4)/2
const int MAX_BALL = , MAX_NODE = MAX_BALL * , MAX_EDGE = , TOT_BALL = ; struct Dinic
{
struct Edge;
struct Node; struct Node
{
int Id, Level;
Edge* Head;
Edge *DfsFrom;
bool Deled;
Node() { Deled = false; }
}; struct Edge
{
int Cap, OrgCap;
Node *From, *To;
Edge *Next, *Rev;
Edge(int cap, Node *from, Node *to, Edge *next) :Cap(cap), OrgCap(cap), From(from), To(to), Next(next) {}
}; Node _nodes[MAX_NODE];
Edge *_edges[MAX_EDGE];
int _vCount, _eCount;
Node *StartNode, *TargetNode; void Init(int sId, int tId, int vCount)
{
memset(_nodes, , sizeof(_nodes));
memset(_edges, , sizeof(_edges));
_eCount = ;
_vCount = vCount;
StartNode = sId + _nodes;
TargetNode = tId + _nodes;
} void Reuse(Node *cur)
{
cur->Deled = false;
for (Edge *e = cur->Head; e; e = e->Next)
{
if (!e->To->Deled)
{
e->Cap = e->OrgCap;
e->Rev->Cap = e->Rev->OrgCap;
}
}
} void ReuseById(int id)
{
Reuse(id + _nodes);
} Edge* AddEdge(Node *from, Node *to, int eCap)
{
assert(from != to);
Edge *cur = _edges[++_eCount] = new Edge(eCap, from, to, from->Head);
cur->From->Head = cur;
return cur;
} void DeleteNode(Node *cur)
{
cur->Deled = true;
for (Edge *e = cur->Head; e; e = e->Next)
{
e->Cap = e->Rev->Cap = ;
}
} void DeleteById(int id)
{
DeleteNode(id + _nodes);
} void Build(int uId, int vId, int eCap)
{
Node *u = uId + _nodes, *v = vId + _nodes;
u->Id = uId;
v->Id = vId;
Edge *edge1 = AddEdge(u, v, eCap), *edge2 = AddEdge(v, u, );
edge1->Rev = edge2;
edge2->Rev = edge1;
} struct NodeQueue
{
Node *q[MAX_NODE];
int head, tail;
void clear() { head = tail = ; }
void push(Node *v) { q[tail++] = v; }
Node* front() { return q[head]; }
void pop() { head++; }
bool empty() { return head == tail; }
}; bool Bfs()//常规,从源点构造
{
for (int i = ; i <= _vCount; i++)
_nodes[i].Level = ;
static NodeQueue q;
q.clear();
StartNode->Level = ;
q.push(StartNode);
while (!q.empty())
{
Node *u = q.front();
q.pop();
for (Edge *e = u->Head; e; e = e->Next)
{
assert(e->Cap >= );
if (!e->To->Level && e->Cap)
{
e->To->Level = u->Level + ;
q.push(e->To);
}
}
}
return TargetNode->Level;//遗忘点
} int Dfs(Node *cur, int limit)
{
if (cur == TargetNode)
return limit;
if (limit == )
return ;
int curTake = ;
for (Edge *e = cur->DfsFrom; e; cur->DfsFrom = e = e->Next)
{
if (e->To->Level == cur->Level + && e->Cap)
{
int nextTake = Dfs(e->To, min(limit - curTake, e->Cap));
e->Cap -= nextTake;
e->Rev->Cap += nextTake;
curTake += nextTake;
}
if (limit - curTake == )
break;
//assert(e->Cap == 0);
}
return curTake;
} int Proceed()
{
int ans = ;
while (Bfs())
{
for (int i = ; i <= _vCount; i++)
_nodes[i].DfsFrom = _nodes[i].Head;
ans += Dfs(StartNode, INF);
#ifdef test
printf("ans=%d\n", ans);
#endif
}
//printf("ans %d\n", ans);
return ans;
}
}g;
Dinic::Edge *ts; bool IsSquare(int x)
{
int a = floor(sqrt(x) + 0.5);
return a*a == x;
} bool Judge(int ballCnt)
{
//printf("ballCnt %d\n", ballCnt);
g._vCount = ballCnt * + ;
ts->Cap = ts->OrgCap;
ts->Rev->Cap = ts->Rev->OrgCap;
for (int ball = ballCnt + ; ball <= TOT_BALL; ball++)
{
g.DeleteById(P(ball));
g.DeleteById(Q(ball));
}
for (int ball = ; ball <= ballCnt; ball++)
{
g.ReuseById(P(ball));
g.ReuseById(Q(ball));
}
return g.Proceed() == ballCnt;
} int Bsearch(int maxL, int maxR)
{
int l = maxL, r = maxR, ans = -;
while (l <= r)
{
int mid = (l + r) / ;
if (Judge(mid))
l = (ans = mid) + ;
else
r = mid - ;
}
return ans;
} void InitBuild(int totPole)
{
int sId = , tId = , orgS = , orgT = ;
g.Init(sId, tId, );
g.Build(orgT, orgS, totPole);
ts = g._edges[g._eCount - ];
for (int ballNum = ; ballNum <= TOT_BALL; ballNum++)
{
g._vCount += ;
g.Build(sId, Q(ballNum), );
g.Build(P(ballNum), tId, );
g.Build(orgS, P(ballNum), );
g.Build(Q(ballNum), orgT, );
for (int i = ; i < ballNum; i++)
if (IsSquare(i + ballNum))
g.Build(Q(i), P(ballNum), );
}
} void Print(int ballNum)
{
static bool Vis[MAX_BALL];
for (int i = ; i <= ballNum; i++)
{
if (Vis[i])
continue;
Dinic::Node *p = P(i) + g._nodes, *q = Q(i) + g._nodes;
bool stop = false;
while (!stop)
{
Vis[arcP(p->Id)] = true;
printf("%d ", arcP(p->Id));
stop = true;
for (Dinic::Edge *e = q->Head; e; e = e->Next)
{
if (arcP(e->To->Id) <= ballNum)
//printf("print %d' - %d cap %d\n", arcQ(q->Id), arcP(e->To->Id), e->Cap);
if (!e->Cap && <= arcP(e->To->Id) && arcP(e->To->Id) <= ballNum && !Vis[arcP(e->To->Id)] && !e->To->Deled)
{
stop = false;
p = e->To;
q = Q(arcP(p->Id)) + g._nodes;
break;
}
}
}
printf("\n");
}
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
int totPole;
scanf("%d", &totPole);
InitBuild(totPole);
int ballNum = Bsearch(, TOT_BALL);
printf("%d\n", ballNum);
Judge(ballNum);
Print(ballNum);
return ;
}

luogu2765 魔术球问题 网络流的更多相关文章

  1. Libre 6003 「网络流 24 题」魔术球 (网络流,最大流)

    Libre 6003 「网络流 24 题」魔术球 (网络流,最大流) Description 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球. (1)每次只 ...

  2. P2765 魔术球问题 网络流二十四题重温

    P2765 魔术球问题 知识点::最小点覆盖 这个题目要拆点,这个不是因为每一个球只能用一次,而是因为我们要求最小点覆盖,所以要拆点来写. 思路: 首先拆点,然后就是开始建边,因为建边的条件是要求他们 ...

  3. P2765 魔术球问题 (网络流)

    题意:n根柱子 把编号1,2,3....的球依次插到柱子上去 需要满足相邻的两个球编号加起来为完全平方数 n < 55 题解:网络流24(23)题里的 但是一直不知道怎么建图  或者说建图的意义 ...

  4. luogu2765 魔术球问题

    发现好像没人来证明贪心啊--那我来写一下它的证明 欲证明:放一个数在已有的柱上(如果可以)总是比新开一个柱更优的 假如已经放了x1..x2....xu..xv..xw.... 现在我要放xx 我有两种 ...

  5. LOJ6003 - 「网络流 24 题」魔术球

    原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...

  6. LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

    6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

  7. [loj #6003]「网络流 24 题」魔术球 二分图最小路径覆盖,网络流

    #6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...

  8. cogs_396_魔术球问题_(最小路径覆盖+二分图匹配,网络流24题#4)

    描述 http://cojs.tk/cogs/problem/problem.php?pid=396 连续从1开始编号的球,按照顺寻一个个放在n个柱子上,\(i\)放在\(j\)上面的必要条件是\(i ...

  9. P2765 魔术球问题

    P2765 魔术球问题 贪心模拟就可以过.........好像和dinic没啥关系   找找规律发现可以贪心放.n又灰常小. 设答案=m 你可以$O(mn)$直接模拟过去 闲的慌得话可以像我用个$se ...

随机推荐

  1. apk的包名修改

    今天,想在android手机上安装两个相同的应用,本以为可以安装不同版本的,试了几次,均相互覆盖了,于是,只能设法修改apk所对应的包名(package name). 目的声明:本文只是为了满足DIY ...

  2. CAD在网页中绘制批注

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...

  3. B.3 字典

    在框架中,字典的选择要比列表少得多.只有三个主流的非并发 IDictionary<TKey, TValue> 实现,此外还有 ExpandoObject (第14章已介绍过). Concu ...

  4. 笔记,js对象浅析

    学习笔记, 来源:http://www.cnblogs.com/zuiyirenjian/p/3535126.html 作者:醉意人间  此外,关于自运行函数可参考  http://benalman. ...

  5. hbase + phoenix 单机版安装

    1. 环境: centos 6.5 jdk 1.8.0                                                 http://www.oracle.com/te ...

  6. nexus3的安装和使用

    参考:https://www.cnblogs.com/2YSP/p/9533506.html http://www.54tianzhisheng.cn/2017/10/14/Nexus3-Maven/ ...

  7. Linux - nginx+uWSGI+django+virtualenv+supervisor发布web服务器

    目录 Linux - nginx+uWSGI+django+virtualenv+supervisor发布web服务器 crm django项目部署流程 使用supervisro启动uwsgi,退出虚 ...

  8. PAT 1101 Quick Sort

    There is a classical process named partition in the famous quick sort algorithm. In this process we ...

  9. PAT 1091. Acute Stroke (bfs)

    One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the re ...

  10. JavaScript基本知识----操作符,流程控制,循环,字符串方法,数组方式,补充方法

    操作符 算术运算 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...