【还在Hungary?你Out了~】
今天上午被下面这道题虐了很久..完全没想到是二分图。
迷宫
根据藏宝地图,cj的科学家们找到了藏宝地点,但却发现入口有多个,谁都没敢冒然进入。于是大家停下脚步,想应该从哪一个门进入。突然,图形学家Mercury发现了隐藏在藏宝地图上的秘密,果然有意识呀!他发现,这个地图在暗处时会显现出一个有向无环图(至于为什么有向,我想是因为某种特殊作用力的缘故吧。),而这个图正是这个迷宫的地图。幸亏没有冒然进入,要不然就出不来了。
但是这个地图却没说出口在哪里,哎,只好用Jupiter的超级机器人进洞寻找了。每一个入口将把机器人瞬间传送到对应编号的点(所有点都能到达),为了不让Jupiter同学失去太多他心爱的机器人,我们必须用最少的机器人去搜索每个点(每个点只能被经过一次)。
输入
第一行两个数n、m,分别表示地图的点数和边数,接下来m行,每行两个数u、v,表示有一条从点u到点v的有向边。
输出
只有一个数,表示最少要用的机器人数。
输入样例
3 2
1 2
2 3
输出样例
1
数据范围
n<=200000
m<=500000
被邓大牛点醒,一看, 啊,还真是,只是这数据范围...
于是开始学习Hopcroft Karp
说起来其实也不难,
回想我们在做Hungary的时候,每次只增广一条路,如何优化呢?很简单,就是一次多增广。
流程如下:
①BFS找出极短增广路集,当最短增广路集为∅时退出
{
注意:这一句话我理解了很久,其意思实际上是对于所有未覆盖的点,预先找一条“增广路”。这条路可能实际上不增广。那么就要把原来的匹配取消。
怎么取消呢?就是这一段
else
{
dx[my[i]] = dy[i] + 1;
que[tail++] = my[i];
}
为什么改变距离标号就可以了呢?请看下文
}
②DFS,尝试改菜的每条增广路,此事要运用一种类似于Dinic的思想——只有距离标号差为1的才能增广。
就是这段
if (dy[i] == dx[x] + 1)
{
dy[i] = 0;
if (my[i] == -1 || dfs(my[i]))
{
mx[x] = i;
my[i] = x;
return 1;
}
}
这实际上已经很像匈牙利算法了。
这样算法的复杂度降到了O(sqrt(N) * E) ,其中N为点数,E为边数,
证明援引自http://hi.baidu.com/lhdpcbjjzjhjtyr/item/0fda4d43c100ad0bc0161323
以下:
设M是一个匹配,p是关于M的一条最短增广轨,p'是关于M^p的一条最短增广轨,那么 p' >= p + 2;
证明:几乎是废话,一条增广轨的长度是奇数,两个奇数当然至少相差2。
一共只用进行O( sqrt(V) )轮。
证明:设当前的最短增广轨长度L=sqrt(n),显然到达这个长度只需要进行L/2个phase。设此时的匹配为M,而最大匹配为M*,则|M*| - |M| <= n / (sqrt(n) + 2) <= sqrt(n)。如果后续每阶段只共现一条增广轨,那么也只要sqrt(n)次就好,所以一共需要3/2sqrt(n)轮增广。
上面的证明用到了一个基本定理,即对于任意两个匹配M1和M2,如果|M1| > |M2|,则M1^M2有至少|M1| - |M2|条不相交的增广轨。
下三个分别是:Wiki版PAs和WIkiPAS翻译版WIKICPP和此题源代码
const maxn=1000;
var dx,dy,mx,my,q:array[1..maxn]of longint;
adj:array[1..maxn,0..maxn]of longint;
n,m,e,i,j,ans,ff,rr:longint;
function bfs:boolean;
var i,u,j:longint;
begin
bfs:=false;
fillchar(q,sizeof(q),0);
rr:=1;
ff:=1;
for i:=1 to n do
if mx[i]=-1
then begin
q[ff]:=i;
inc(ff);
end;
for i:=1 to n do dx[i]:=0;
for i:=1 to m do dy[i]:=0;
while rr
#include
#include
#include
#include
#include
#ifdef WIN32
#define ot "%I64d"
#else
#define ot "%lld"
#endif
#define max(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})
#define maxn 1005
using namespace std;
int dx[maxn], dy[maxn], mx[maxn], my[maxn], que[maxn];
int adj[maxn][maxn];
int n, m, ans, e;
void init()
{
freopen("hk.in", "r", stdin);
freopen("hk.out", "w", stdout);
scanf("%d%d%d", &n, &m, &e);
memset(adj, 0, sizeof(adj));
for (int i = 1; i <= e; i++)
{
int f, r;
scanf("%d%d", &f, &r);
adj[f][++adj[f][0]] = r;
}
memset(mx, -1, sizeof mx);
memset(my, -1, sizeof my);
}
bool bfs()
{
bool rt = 0;
memset(que, 0, sizeof(que));
int head = 1, tail = 0;
for (int i = 1; i <= n; i++)
if (mx[i] == -1) que[++tail] = i;
memset(dx, 0, sizeof(dx));
memset(dy, 0, sizeof(dy));
while (head <= tail);
{
int u = que[head++];
for (int j = 1; j <= adj[u][0]; j++)
{
int i = adj[u][j];
if (!dy[i])
{
dy[i] = dx[u] + 1;
if (my[i] == -1) rt = 1;
else
{
dx[my[i]] = dy[i] + 1;
que[tail++] = my[i];
}
}
}
}
return rt;
}
bool dfs(int x)
{
for (int j = 1; j <= adj[x][0]; j++)
{
int i = adj[x][j];
if (dy[i] == dx[x] + 1)
{
dy[i] = 0;
if (my[i] == -1 || dfs(my[i]))
{
mx[x] = i;
my[i] = x;
return 1;
}
}
}
return 0;
}
int main()
{
init();
ans = 0;
while (bfs())
for (int i = 1; i <= n; i++)
if (mx[i] == -1 && dfs(i)) ++ans;
return 0;
}
#include
#include
#include
#include
#include
#include
#define maxe 500005
#define maxn 200005
using namespace std;
int h[maxn], nt[maxe], t[maxe];
int mx[maxn], my[maxn], dx[maxn], dy[maxn];
int n, m, tot = 0, ans = 0;
void adde(int a, int b)
{
t[++tot] = b; nt[tot] = h[a]; h[a] = tot;
}
void init()
{
freopen("maze.in", "r", stdin);
scanf("%d%d", &n, &m);
int u, v;
for (int i = 1; i <= m; i++)
scanf("%d%d", &u, &v), adde(u, v);
memset(mx, -1, sizeof mx);
memset(my, -1, sizeof my);
}
void outit()
{
freopen("maze.out", "w", stdout);
printf("%d", n - ans);
}
int que[maxn], head, tail;
bool bfs()
{
head = 1, tail = 0;
bool rt = 0;
for (int i = 1; i <= n; i++)
if (mx[i] == -1) que[++tail] = i;
memset(dx, 0, sizeof dx);
memset(dy, 0, sizeof dy);
while (head <= tail)
{
int p = que[head++];
for (int q = h[p]; q; q = nt[q])
{
int r = t[q];
if (!dy[r])
{
dy[r] = dx[p] + 1;
if (my[r] == -1) rt = 1;
else
{
dx[my[r]] = dy[r] + 1;
que[++tail] = my[r];
}
}
}
}
return rt;
}
bool dfs(int p)
{
for (int q = h[p]; q; q = nt[q])
{
int r = t[q];
if (dy[r] == dx[p] + 1)
{
dy[r] = 0;
if (my[r] == -1 || dfs(my[r]))
{
mx[p] = r; my[r] = p;
return 1;
}
}
}
return 0;
}
bool select[maxn];
void got(int i)
{
for (int q = h[i]; q; q = nt[q])
if (!select[t[q]])
{
++ans;
mx[i] = t[q]; my[t[q]] = i;
select[i] = 1, select[t[q]] = 1;
return ;
}
}
int main()
{
init();
ans = 0;
memset(select, 0, sizeof select);
for (int i = 1; i <= n; i++)
if (!select[i]) got(i);
while (bfs())
for (int i = 1; i <= n; i++) if (mx[i] == -1) if (dfs(i)) ++ans;
outit();
return 0;
}