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 ...
随机推荐
- Ajax——瀑布流
基本概念 1.宽度是一致的,高度上参差不齐 2.新增内容优先放置在最矮的地方 核心难点 1.用一个数组存储每列的高度值 2.新值添加到值最小索引上,每次替换更新数组 插件使用 1.$.fn.exten ...
- 【译】x86程序员手册08 -2.6中断和异常
2.6 Interrupts and Exceptions 中断和异常 The 80386 has two mechanisms for interrupting program execution: ...
- C#斐波那契数列方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- c#仿照qq登录界面编辑框内容操作
using System; using System.Drawing; using System.Windows.Forms; namespace 案例演示 { public partial clas ...
- day01-编程与计算机组成原理
什么是编程 编程语言:是人与计算机沟通交流的介质,通过标准化的规则传递信息 编程:就是为了使计算机能够理解人的意图,通过编程语言写出一个个文件,这堆文件完成相应的目的 编程的目的:用计算机取代人完成工 ...
- VUE路由history模式坑记--NGINX
因微信分享和自动登录需要,对于URL中存在'#'的地址,处理起来比较坑(需要手动写一些代码来处理).还有可能会有一些隐藏的问题没被发现. 如果VUE能像其他(JSP/PHP)系统的路径一样,就不存在这 ...
- 远程连接阿里云服务器ping不通ip解决方案
搭建了阿里云服务器,发现本地ping不通,查看半天才发现,原来是在阿里云上的安全组少了些东西. 在出入方向上新建一个安全组,就可以搞定了.
- js的基础运用
总结: 1.定义:分为隐式定义和显式定义可以先定义后赋值. 2.+:当两边都是数值则运行加法运算,若一遍是字符型则进行拼接. 3.数值变字符:数值变量.toString()方法. 字符变数值:通过加一 ...
- 最快的 Python Web 框架入门
速度比较 框架 实现基础 每秒请求数 平均时间 Sanic Python 3.5 + uvloop 30,601 3.23ms Wheezy gunicorn + meinheld 20,244 4. ...
- 1 Excel
#region 设置页边距 //sheet.SetMargin(MarginType.LeftMargin, (double)0.6 / 3); //sheet.SetMargin(MarginTyp ...