/* THE PROGRAM IS MADE BY PYY */
/*----------------------------------------------------------------------------//
Copyright (c) 2012 panyanyany All rights reserved.
URL : http://poj.org/problem?id=3189
Name : 3189 Steady Cow Assignment
Date : Monday, February 13, 2012
Time Stage : 5 hours
Result:
9802586 panyanyany
3189
Accepted 948K 329MS C++
4870B 2012-02-13 21:00:17
Test Data :
Review :
题意理解不到位,wa无数次,数组开错,继续WA……
发现每次做网络流都要3个小时以上……好悲剧……
以后最多做两个小时,不行就先放下,花太多时间效果又不好,太不值得了……
这题不是单纯的二分枚举,思维定势了,老是理解不了,看了答案也不明白。
想了很久,才终于想到,二分枚举的只是区间长度,还要在每次二分枚举的时候向右
移动那个定区间。
如题目中的例子一样,可以看作是x轴上有四个单位,假如某次枚举的时候,枚举到2个单位,
也就是说,可能是[1,2],[2,3],[3,4]。也就是说,2个单位的时候,要做三次图,第一次是每个牛
只能选择自己第1喜欢和第2喜欢的棚,第二次是每个牛只能选择自己第2喜欢和第3喜欢的棚……
//----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define MEM(a, v) memset (a, v, sizeof (a)) // a for address, v for value
#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))
#define INF (0x3f3f3f3f)
#define MAXN (1002*2)
#define MAXE (MAXN*22*4)
#define DB /##/
struct EDGE {
int u, v, c, n ;
};
int n, b, eCnt, s, t ;
int dist[MAXN], q[MAXN], vertex[MAXN], barn[MAXN][22], cap[22] ;
EDGE edge[MAXE] ;
inline void init()
{
eCnt = 0 ;
MEM (vertex, -1) ;
}
inline void insert (const int u, const int v, const int c)
{
edge[eCnt].u = u ;
edge[eCnt].v = v ;
edge[eCnt].c = c ;
edge[eCnt].n = vertex[u] ;
vertex[u] = eCnt++ ;
edge[eCnt].u = v ;
edge[eCnt].v = u ;
edge[eCnt].c = 0 ;
edge[eCnt].n = vertex[v] ;
vertex[v] = eCnt++ ;
}
int dinic (int beg, int end)
{
int ans = 0 ;
while (true)
{
int head, tail, u, v, e ;
MEM(dist, -1) ;
head = tail = 0 ;
q[tail++] = beg ;
dist[beg] = 0 ;
// 广搜,构建层次图
while (head < tail)
{
v = q[head++] ;
for (e = vertex[v] ; e != -1 ; e = edge[e].n)
{
u = edge[e].u ;
int to = edge[e].v ;
int cost = edge[e].c ;
if (cost > 0 && dist[to] == -1)
{
dist[to] = dist[u] + 1 ;
q[tail++] = to ;
if (to == end)
{
head = tail ;
break ;
}
}
}
}
if (dist[end] == -1)
break ;
// v 表示增广路径的先头顶点
v = beg ;
tail = 0 ;
while (true)
{
DB printf("--- tail:%d ", tail) ;
if (v == end)
{
int i, flow = INF, ebreak ;
// 寻找此路径可增加的最大流量
for (i = 0 ; i < tail ; ++i)
if (flow > edge[q[i]].c)
{
flow = edge[q[i]].c ;
ebreak = i ;
}
ans += flow ;
// 根据刚才找到的最大流,更新此路径上的所有边
for (i = 0 ; i < tail ; ++i)
{
edge[q[i]].c -= flow ; // 正向边减流
edge[q[i]^1].c += flow ; // 反向边加流
}
// 增广路径的先头顶点退至0流量的正向边的起始顶点
v = edge[q[ebreak]].u ;
tail = ebreak ;
DB printf ("end --- v:%d ebreak:%d, ans:%d\n", v, ebreak, ans) ;
}
// 寻找有无可以继续增广的边
// 即,测试所有从顶点 v 起始的边中,是否有可以增广的边
// find a way from e to any vertex in "layers"
for (e = vertex[v] ; e != -1 ; e = edge[e].n)
{
// 为了避免 -1 + 1 == 0 的情况,需要测试 dist[edge[e].u] > -1
// 其实这一步貌似可以省略,因为既然能够作为增广路径的先头顶点,
// 其必然就在层次图中,因此 dist[u] 也就一定会 大于 -1
if (edge[e].c > 0 && //dist[edge[e].u] > -1 &&
dist[edge[e].u]+1 == dist[edge[e].v])
{
// printf ("dist[%d]+1 == dist[%d]: %d+1 == %d\n",
// edge[e].u, edge[e].v, dist[edge[e].u], dist[edge[e].v]) ;
break ;
}
}
DB printf ("v:%d, e:%d, edge[%d]: u:%d, v:%d, c:%d, n:%d\n", \
v, e, e, edge[e].u, edge[e].v, edge[e].c, edge[e].n) ;
// system ("pause 1>>nul 2>>nul") ;
// 不能从 vertex[v] 所指向的边找到增广路
if (e == -1) // no way from current edge's next vertex
{
// 路径队列中已经没有边了
if (tail == 0) // no edges in queue
break ;
// 既然 vertex[v] 所指向的边已经无路可通了
// 那么就应该把该边的目的顶点从层次图中删除
// 一开始写成了 dist[edge[q[--tail]].u] = -1
// 结果一直死循环……本程序所有的注释代码,都是为此错误服务的……
dist[edge[q[--tail]].v] = -1 ;
// 增广路径退一条边,回到 vertex[v] 所在边的前一个顶点
v = edge[q[tail]].u ; // backward to previous vertex
DB printf ("e == -1 ----- v:%d, tail:%d\n", v, tail) ;
}
else // put the edge in queue
{
// 发现一条边可用,于是加入到增广路径队列中
q[tail++] = e ;
// 将新边的目的顶点设为增广路径的先头顶点
v = edge[e].v ;
}
DB puts ("") ;
}
}
return ans ;
}
int makegraph (const int lim)
{
int i, j, low ;
for (low = 1 ; low <= b - (lim - 1) ; ++low)
{
init();
for (i = 1 ; i <= n ; ++i)
{
insert (s, i, 1) ;
for (j = low ; j <= low + (lim-1) ; ++j)
insert (i, barn[i][j]+n, 1) ;
}
for (i = 1 ; i <= b ; ++i)
insert (i+n, t, cap[i]) ;
if (dinic(s, t) == n)
return true ;
}
return false ;
}
int main()
{
int i, j ;
int ans, low, hig, mid ;
while (scanf("%d%d", &n, &b) != EOF)
{
for (i = 1 ; i <= n ; ++i)
{
for (j = 1 ; j <= b ; ++j)
{
scanf ("%d", &barn[i][j]) ;
}
}
for (i = 1 ; i <= b ; ++i)
scanf ("%d", &cap[i]) ;
s = 0 ;
t = n+b+1 ;
low = 1 ;
hig = b ;
ans = -1 ;
while (low <= hig)
{
mid = (low + hig) / 2 ;
if (makegraph (mid))
{
ans = mid ;
hig = mid - 1 ;
}
else
low = mid + 1 ;
}
printf ("%d\n", ans) ;
}
return 0 ;
}
分享到:
相关推荐
最大流的Dinic算法,时间复杂度O(EV^2),代码简单而高效
[Din70]Algorithm for solution of a problem of maximum flow in a network with power estimation.pdf 最大流最高标号法(DINIC法)的论文原文
最大流问题在图论和网络优化中占有重要地位,它旨在寻找从源点到汇点在网络中的最大流量,而不会超过任何边的容量限制。在这个问题的求解中,Dinic算法和Shortest Augmenting Path (SAP)算法是两种高效的方法。 ...
【二分图顶点覆盖->最小割->最大流->Dinic算法求解】 解题报告+AC代码 http://hi.csdn.net/!s/WKVPR0 ----> 我的所有POJ解题报告 http://blog.csdn.net/lyy289065406/article/details/6642573
最大网络流问题在图论和运筹学中占有重要地位,它主要研究在一个有向图中,从源点到汇点能通过的最大流量。Dinic算法是由苏联计算机科学家E. A. Dinic于1970年提出的,是一种解决此类问题的有效算法。该算法基于层的...
Dinic于1970年提出的求解最大流问题的有效算法,它的主要特点是基于层的概念和增广路径的思想。下面我们将深入探讨Dinic算法的基本原理和步骤。 1. **初始化**:首先定义源点s和汇点t,将所有边的容量设为边上的...
用DINIC方法实现最大流算法,亲测可以运行!VS2008环境下编辑运行通过!
Dinic算法的基本思路: 1.根据残量网络计算层次图。 2.在层次图中使用DFS进行增广直到不存在增广路 3.重复以上步骤直到无法增广
Dinic算法是一种高效的求解最大流问题的方法,通过构建层次化的残余网络并利用DFS寻找增广路径来不断更新流量,最终找到网络的最大流。通过对算法的深入理解与实践,可以帮助我们在实际问题中更好地应用这一算法。
网络流dinic模板,非本人原创。网络流dinic模板,非本人原创
本文将详细介绍Dinic算法,这是一种高效的求解网络最大流的算法。 首先,我们要理解什么是最大流。在网络流问题中,我们有一个带权重的有向图,其中每个边代表一个可以传输流量的连接,每条边都有一个容量限制。...
### 非递归邻接表DINIC最大流算法详解 #### 一、引言 在图论中,寻找网络中的最大流量是一项经典问题。其中,DINIC算法(由Yefim Dinitz于1970年提出)是一种高效解决这类问题的方法。本文将详细介绍一个非递归版本...
** Dinic网络流算法 ** Dinic算法,由苏联计算机科学家E. M. Dinic于1970年提出,是一种求解网络最大流问题的高效算法。在图论和运筹学中,网络流问题涉及到在一个有向图中从一个源节点到一个汇点传输“流量”,...
最大流模板.cpp dinic
【标题】"POJ2195-Going Home" 是一道来自北京大学在线判题系统POJ的编程题目,主要考察的是运用费用流算法解决实际问题的能力。这道题的中文名称是“回家”,通过题目我们可以推测它可能涉及到路径规划或者资源分配...
给师弟师妹们讲网络流Dinic算法与可行流用到的讲义,感觉还是不错,分享一下~
最大流的 Dinic 算法的 C++ 实现。 以下是操作摘要: FlowNetwork f(n, m) :具有 n 个顶点(0 到 n-1)和 m 个有向边的新网络, f.add(x, y, c) :添加从节点 x 到节点 y 的有向边,容量为 c, f.flow(s, t) :...
Dinic算法是一种高效的求解最大流问题的方法,基于Ford-Fulkerson算法的基础上改进而来。它利用了BFS(广度优先搜索)来寻找增广路径,并通过DFS(深度优先搜索)来进行流量的推送。相较于其他最大流算法,Dinic算法...
Edmonds-Karp算法和Dinic算法是解决这类问题的两种著名算法,它们都用于寻找一个图的最大流。下面将详细讨论这两个算法以及它们在C++中的实现。 首先,让我们了解Edmonds-Karp算法。这个算法基于增广路径的概念,即...