luogu2765 魔术球问题 网络流
题目大意:
假设有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 魔术球问题 网络流的更多相关文章
- Libre 6003 「网络流 24 题」魔术球 (网络流,最大流)
Libre 6003 「网络流 24 题」魔术球 (网络流,最大流) Description 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球. (1)每次只 ...
- P2765 魔术球问题 网络流二十四题重温
P2765 魔术球问题 知识点::最小点覆盖 这个题目要拆点,这个不是因为每一个球只能用一次,而是因为我们要求最小点覆盖,所以要拆点来写. 思路: 首先拆点,然后就是开始建边,因为建边的条件是要求他们 ...
- P2765 魔术球问题 (网络流)
题意:n根柱子 把编号1,2,3....的球依次插到柱子上去 需要满足相邻的两个球编号加起来为完全平方数 n < 55 题解:网络流24(23)题里的 但是一直不知道怎么建图 或者说建图的意义 ...
- luogu2765 魔术球问题
发现好像没人来证明贪心啊--那我来写一下它的证明 欲证明:放一个数在已有的柱上(如果可以)总是比新开一个柱更优的 假如已经放了x1..x2....xu..xv..xw.... 现在我要放xx 我有两种 ...
- LOJ6003 - 「网络流 24 题」魔术球
原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...
- LibreOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖
6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...
- [loj #6003]「网络流 24 题」魔术球 二分图最小路径覆盖,网络流
#6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...
- cogs_396_魔术球问题_(最小路径覆盖+二分图匹配,网络流24题#4)
描述 http://cojs.tk/cogs/problem/problem.php?pid=396 连续从1开始编号的球,按照顺寻一个个放在n个柱子上,\(i\)放在\(j\)上面的必要条件是\(i ...
- P2765 魔术球问题
P2765 魔术球问题 贪心模拟就可以过.........好像和dinic没啥关系 找找规律发现可以贪心放.n又灰常小. 设答案=m 你可以$O(mn)$直接模拟过去 闲的慌得话可以像我用个$se ...
随机推荐
- PHP中单例模式与工厂模式
单例模式概念 单例模式是指整个应用中类只有一个对象实例的设计模式. 单例模式的特点 一个类在整个应用中只有一个实例 类必须自行创建这个实例 必须自行向整个系统提供这个实例 php中使用单例模式的原因 ...
- JS——Boolean(逻辑)对象
Boolean(逻辑)对象用于将非逻辑值转换为逻辑值(true 或者 false). 创建 Boolean 对象的语法: new Boolean(value); //构造函数 Boolean(valu ...
- [Windows Server 2008] 安装SQL SERVER 2008
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:安装SQL S ...
- Java_Web三大框架之Hibernate+jsp+selvect+HQL查询数据
俗话说:"好记性不如烂笔头".本人学习Hibernate也有一个星期了,对Hibernate也有一个初步的了解.下面对Hibernate显示数据做个笔记,使用租房系统的Hibern ...
- 神经网络图灵机(Neural Turing Machines, NTM)
近期,Google Deep Mind团队提出了一个机器学习模型,并起了一个特别高大上的名字:神经网络图灵机,我为大家翻译了这篇文章,翻译得不是特别好,有些语句没读明白,欢迎大家批评指正 原论文出处 ...
- Java单元测试 - TestNG
官网 Eclipse安装TestNG插件 与Junit相比 从Junit发展而来,开发者就是Junit小组的一个人 Test Suite不再需要硬编码,就像cf自动登录的脚本中一样,可以写到一个xml ...
- 读取json文件并把uft-8转换为ascii
#!/usr/bin/python import sys import json as js import codecs import collections #reload(sys) #sys.se ...
- (蓝桥)2017C/C++A组第一题迷宫
#include<iostream> #include<memory.h> using namespace std; char mi[10][10] ; int visited ...
- 《零压力学Python》 之 第一章知识点归纳
第一章(初识Python)知识点归纳 Python是从ABC语言衍生而来的 ABC语言是Guido参与设计的一种教学语言,为非专业编程人员所开发的. Python是荷兰程序员 Guido Van Ro ...
- ansible plugins 列表
[action plugins] [cache plugins]jsonfilememcachedmemorymongodbpickleredisyaml [callback plugins]acti ...