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 ...
随机推荐
- Java引用jar的优化
一般java的类文件开头都是各种引用: 如 上图的引用可以写成
- JS——math
random() 方法可返回介于 0 ~ 1 之间的一个随机数. Math.random() 0.0 ~ 1.0 之间的一个伪随机数,但是不包括0和1.
- Java_Web三大框架之Hibernate+jsp+selvect+HQL登入验证
刚开始接触Hibernate有些举手无措,觉得配置信息太多.经过一个星期的适应,Hibernate比sql简单方便多了.下面做一下Hibernate+jsp+selvect+HQL登入验证. 第一步: ...
- CentOS安装Nodejs-v8.11.1
(1)到NodeJs官网(https://nodejs.org/en/download/),复制下载链接 (2)下载并解压 命令:wget https://nodejs.org/dist/v8.11. ...
- H5及微信中唤起app的解决方案
今天我们就来说说这个callapp-lib 我的接到的需求大概是这样的 如果检测到不是在app里面用webview打开的页面就会显示上面的立即打开按钮, 点击的话会判断是否在微信中, 如果在微信中打开 ...
- POJ-2135-Farm Tour(最大费用最小流)模板
Farm Tour POJ - 2135 When FJ's friends visit him on the farm, he likes to show them around. His farm ...
- (C/C++学习)9.C/C++优化排序
说明:在C/C++中常见的排序方法有两种,第一种为选择排序法,第二种为冒泡排序法,本文将对这两种排序法进行优化,并给出一种更为快捷的排序法. 一.未优化的排序法 现在假如要对一个数组进行排序,假设这个 ...
- Scrapy实战:使用IDE工具运行爬虫
一般我们运行爬虫程序都是使用命令行,比如:scrapy crwal sobook.不过这多少有些不方便,可以使用下面的方法使用IDE的方式运行爬虫 我这边使用的是pycharm软件,在pycharm里 ...
- time、datatime模块
python中时间日期格式化符号 %Y 年份(4位数表示) %y 年份(2位数表示) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数 %I 12小时制小时数 %M 分 ...
- BZOJ 1603 USACO 2008 Oct. 打谷机
[题解] 水题.. 保存连接方式,按顺序处理即可. #include<cstdio> #include<algorithm> using namespace std; int ...