POJ 1904 King's Quest 解题报告

给定一个二分图和一个二分图上的完备匹配求出任意一个x集合中的点的一个匹配,要求去掉该匹配后,剩下的顶点依然可以构成完备匹配

按照x的顺序输出nx行,每行按照y的大小顺序输出。

转化成强联通分量来做。对于每个x点从xi到可以匹配的yi加一条边,然后对已给定的匹配,从yi到xi加一条边。求强联通分量,如果在同一个联通分量里的话,那么是可行的解

原理:由于是一个有向的二分图,所以在任一个强联通分量中x集合和y集合中的点必定是成对出现的。对于其中的xi1,xi2如果有可以与之匹配的yj在一个联通分量中的话,xi1与xi2其实是等效的了。(说明的比太明白,呃,证明能力太差了)

代码如下:

#include <iostream>

#include <cstring>

#include <cstdio>

#include <algorithm>

using namespace std;

class scc

{

private:

	const static int SIZE = 5001;

	const static int EDGE = 1000000;

	struct Edges

	{

		int v;

		Edges *next;

	}pool[EDGE],*g[SIZE],*pp;

	void addedge(int v,int w)

	{

		pp->v = w;

		pp->next = g[v];

		g[v] = pp++;

	}

	void dfs(int x);

	int dfn[SIZE],reach[SIZE],low[SIZE];

	int idx[SIZE],scc_cnt;

	int n,depth,st[SIZE],top;

public:

	bool initialize();

	void solve();

}g;

void scc::solve()

{

        int num[SIZE];

	for(int i = 1;i <= n;i++)

	{

		if(reach[i] == 0)

			dfs(i);

	}

	for(int i = 1;i <= n / 2;i++)

	{

		int sz = 0;

		for(Edges *j = g[i];j != NULL;j = j->next)

		{

			if(idx[j->v] == idx[i])

			{

                                num[sz++] = j->v;

			}

		}

		printf("%d",sz);

                sort(num,num + sz);

		for(int j = 0;j < sz;j++)

			printf(" %d",num[j] - n / 2);

		putchar('\n');

	}

}

void scc::dfs(int x)

{

	st[++top] = x;

	dfn[x] = low[x] = ++depth;

	int w;

	for(Edges * i = g[x];i != NULL;i = i->next)

	{

		w = i->v;

		if(reach[w])

			continue;

		if(dfn[w] == 0)

		{

			dfs(w);

			if(low[w] < low[x])

				low[x] = low[w];

		}

		else if(dfn[w] < low[x])

			low[x] = dfn[w];

	}

	if(low[x] == dfn[x])

	{

		scc_cnt++;

		while(st[top] != x)

		{

			w = st[top--];

			idx[w] = scc_cnt;

			reach[w] = 1;

		}

		w = st[top--];

		idx[w] = scc_cnt;

		reach[w] = 1;

	}

}

bool scc::initialize()

{

	if(scanf("%d",&n) != 1)

		return false;

	memset(dfn,0,sizeof(dfn));

	memset(reach,0,sizeof(reach));

	memset(low,0,sizeof(low));

	memset(g,0,sizeof(g));

	pp = pool;

	scc_cnt = top = depth = 0;

	for(int i = 1;i <= n;i++)

	{

		int j,v;

		scanf("%d",&j);

                for(int k = 0;k < j;k++)

		{

			scanf("%d",&v);

			addedge(i,v + n);

		}

	}

	for(int i = 1;i <= n;i++)

	{

		int v;

		scanf("%d",&v);

		addedge(v + n,i);

	}

	n *= 2;

	return true;

}

int main()

{

	while(g.initialize())

		g.solve();

	return 0;

}



你可能感兴趣的:(poj)