您的位置:首页 > 理论基础 > 计算机网络

cogs396 [网络流24题]魔术球问题简化版

2016-12-24 17:33 260 查看
这道题我一看 第一感觉感觉完全不可做跟网络流完全没有关系……

后来我就弃疗了 看了黄学长的博客QvQ

这道题好神啊

这道题是正确做法是转化为最小路径覆盖问题

我们枚举答案a 对于每一个答案 建点1..a

然后对于i<j且i+j为完全平方数连边 得到一个有向无环图

我们可以发现 每一根柱子其实对应着有向无环图上的一条路径

那么我们对这个有向无环图求最小路径覆盖

就可以算出最少用几根柱子了

实际操作的时候就枚举a 如果最小路径覆盖大于n就输出a-1

顺便说一下DAG的最小路径覆盖

DAG上的最小路径覆盖问题

最小路径覆盖的意思是在一个DAG上找到最少的路径 使得每一个点都只被一条路径覆盖到 单独的一个点也可以算作一个路径

我就不复制百科上的话了= = 自己解释一下

如果DAG有p个点 DAG对应二分图的最大匹配为m

则最小路径覆盖即为p-m

DAG对应二分图的建法是把原图的点p拆成p’和p” 如果原图中存在边pi->pj 那么就把二分图里的p’i和p”j相连 很简单吧= =

做法的正确性在于 我们首先可以发现 如果原图没有边 那么匹配为0 每个点单独做一个路径 最小路径覆盖即为p

每当匹配增加1的时候 就可以把这两个点合到一个路径里 这样最小路径覆盖就会减少1

以此类推 就可以得出结论

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXA=1605,N=MAXA*2,M=200001,S=N-1,T=N-2,INF=1e9;
const double EPS=1e-3;

int n=0;
int head
,next[M],to[M],c[M],f[M],edge=0;
int q[M],front=0,back=0,d
;

inline void addEdge(int u,int v,int argc) {
to[edge]=v,c[edge]=argc,next[edge]=head[u],head[u]=edge++;
to[edge]=u,c[edge]=0,next[edge]=head[v],head[v]=edge++;
}

inline int isPs(int x) {
return abs(sqrt(x)-int(sqrt(x))-0.0)<=EPS;
}

inline void build(int a) {
addEdge(S,2*a-1,1);
addEdge(2*a,T,1);
for (int i=1;i<=a-1;++i) {
if (isPs(i+a))
addEdge(2*i-1,a*2,1);
}
}

int bfs() {
front=back=0;
memset(d,0,sizeof(d));
d[S]=1;
q[back++]=S;
while (front<back) {
int x=q[front++];
for (int e=head[x];~e;e=next[e]) {
int v=to[e];
if (!d[v] && c[e]>f[e]) {
d[v]=d[x]+1;
q[back++]=v;
}
}
}
return d[T];
}

int dfs(int x,int mn) {
if (x==T || !mn)
return mn;
int flow=0;
for (int e=head[x];~e;e=next[e]) {
int v=to[e];
if (c[e]>f[e] && d[v]==d[x]+1) {
int f0=dfs(v,min(mn,c[e]-f[e]));
f[e]+=f0;
f[e^1]-=f0;
flow+=f0;
mn-=f0;
if (!mn)
return flow;
}
}
if (mn)
d[x]=0;
return flow;
}

int dinic() {
int flow=0;
memset(f,0,sizeof(f));
while (bfs())
flow+=dfs(S,INF);
return flow;
}

int main(void) {
freopen("balla.in","r",stdin);
freopen("balla.out","w",stdout);
scanf("%d",&n);
memset(head,-1,sizeof(head));
for (int i=1;i<=n;++i)
build(i);
for (int a=n+1;a<=1600;++a) {
build(a);
if (a-dinic()>n) {
printf("%d\n",a-1);

4000
break;
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流 算法 二分图