【BZOJ2893】征服王

题目

传送门


题解

第一步自然是缩点变成无向图。
然后就是裸的可相交最小路径覆盖。

不可相交最小路径覆盖

即用最少的路径覆盖图中所有点, 路径不可相交。

可用最小割解决。
考虑把原图中的每一个点看成一条单独的路径, 接着不断将路径合并, 形成更长的路径。
那么最后的路径数 = 总点数 - 合并次数(()没合并次减少一条路径()), 要使路径数最小就要让合并次数最多。
可用二分图匹配计算。
把原图中的一个点(u), 拆成(u_1)(u_2);把原图中的边((u, v)), 改成((u_1, v_2))。 那么就构成了一个二分图。在二分图中加一条匹配边等于一次合并, 最大匹配即为最大合并次数。

可相交最小路径覆盖

做法一:
即用最少的路径覆盖图中所有点, 路径可相交。

考虑把在上一个问题的算法直接搬过来。 显然是错的, 因为如果两个点的路径中的任意一个点有被覆盖过, 则两个点就会被无法被合并。
我们要消除这种影响。 换句话说, 只要(A)可到达(B), 那么在二分图就要有一条((A_1, B_2))的边。

然后在求二分图匹配, 即可得到正确的合并次数。

做法二:
最大费用最大流摸拟贪心。


代码

做法一:

#include <iostream>
#include <cstdlib>
#include <stack>
#include <cstdio>
#include <cstring>
#include <bitset>
#include <queue>

using namespace std;

const int N = 1210, M = 200010;

const int INF = 0x3F3F3F3F;

struct edge
{	int from, to, flow, cap;
	edge() { }
	edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), flow(_3), cap(_4) { }
};

namespace Graph
{	edge edges[M];

	int head[N], nxt[M], tot;

	inline void init()
	{	memset(head, -1, sizeof(head));
		tot = 0;
	}

	inline void add_edge(int x, int y)
	{       edges[tot] = edge(x, y, 0, 0);
		nxt[tot] = head[x];
		head[x] = tot++;
	}
}

namespace DAG
{	edge edges[M];

	int head[N], nxt[M], tot;

	inline void init()
	{	memset(head, -1, sizeof(head));
		tot = 0;
	}

	inline void add_edge(int x, int y)
	{	edges[tot] = edge(x, y, 0, 0);
		nxt[tot] = head[x];
		head[x] = tot++;
	}
}

struct Dinic
{	edge edges[N * N * 2];
	int head[2 * N], nxt[N * N * 2], tot;

	inline void init()
	{	memset(head, -1, sizeof(head));
		tot = 0;
	}

	inline void add_edge(int x, int y, int z)
	{	edges[tot] = edge(x, y, 0, z);
		nxt[tot] = head[x];
		head[x] = tot++;
		edges[tot] = edge(y, x, 0, 0);
		nxt[tot] = head[y];
		head[y] = tot++;
	}

	int L, R;

	int s, t;

	int d[2 * N];

	bool bfs()
	{	memset(d, -1, sizeof(d));
		queue<int> q;
		q.push(s);
		d[s] = 0;
		while (!q.empty())
		{	int x = q.front(); q.pop();
			for (int i = head[x]; ~i; i = nxt[i])
			{	edge & e = edges[i];
				if (e.cap > e.flow && d[e.to] == -1)
				{	d[e.to] = d[x] + 1;
					q.push(e.to);
				}
			}
		}
		return d[t] != -1;
	}

	int cur[2 * N];

	int dfs(int x, int a)
	{	if (x == t || a == 0) return a;
		int f, flow = 0;
		for (int & i = cur[x]; ~i; i = nxt[i])
		{	edge & e = edges[i];
			if (d[e.to] == d[x] + 1 && (f = dfs(e.to, min(e.cap-e.flow, a))) > 0)
			{	e.flow += f;
				edges[i^1].flow -= f;
				flow += f;
				a -= f;
				if (a == 0) break;
			}
		}
		return flow;
	}

	int maxflow(int _s, int _t)
	{	s = _s, t = _t;
		int flow = 0;
		while (bfs())
		{	for (int i = L; i <= R; i++)
				cur[i] = head[i];
			flow += dfs(s, INF);
		}
		return flow;
	}
} dinic;

int n, m;

bool bo1[N], bo2[N], boo1[N], boo2[N];

int dfn[N], low[N], dfs_clock;
int num[N], scc;
stack<int> st;

bool boo[N], bo[N];

void Tarjan(int x)
{	using namespace Graph;
	low[x] = dfn[x] = ++dfs_clock;
	st.push(x);
	for (int i = head[x]; ~i; i = nxt[i])
	{	edge & e = edges[i];
		if (!dfn[e.to])
		{	Tarjan(e.to);
			low[x] = min(low[x], low[e.to]);
		}
		else if (!num[e.to]) low[x] = min(low[x], dfn[e.to]);
	}
	if (low[x] == dfn[x])
	{	int v;
		++scc;
		do
		{	v = st.top(); st.pop();
			num[v] = scc;
			if (boo1[v]) bo1[scc] = 1;
			if (boo2[v]) bo2[scc] = 1;
		}
		while(v != x);
	}
}

int out[N], in[N];

int Ans;

queue<int> q;

int node[N], total;

bitset<N> f[N];

void solve()
{	using namespace DAG;
	for (int i = 1; i <= scc; i++)
		if (in[i] == 0 && !bo1[i])
		{	puts("no solution");
			return;
		}
	for (int i = 1; i <= scc; i++)
		if (out[i] == 0 && !bo2[i])
		{	puts("no solution");
			return;
		}
	total = 0;
	while (!q.empty()) q.pop();
	for (int i = 1; i <= scc; i++)
		if (in[i] == 0)
			q.push(i), node[++total] = i;
	while (!q.empty())
	{	int x = q.front(); q.pop();
		for (int i = head[x]; ~i; i = nxt[i])
		{	edge & e = edges[i];
			if ((--in[e.to]) == 0)
				q.push(e.to),
				node[++total] = e.to;
		}
	}
	int S = 2 * scc + 1, T = 2 * scc + 2;
	dinic.L = 0, dinic.R = T;
	dinic.init();
	for (int i = 1; i <= scc; i++)
		dinic.add_edge(S, i, 1);
	for (int i = 1; i <= scc; i++)
		dinic.add_edge(i+scc, T, 1);
	for (int i = total; i >= 1; i--)
	{	int now = node[i];
		f[now][now-1] = 1;
		for (int j = head[now]; ~j; j = nxt[j])
		{	edge & e = edges[j];
			f[now] |= f[e.to];
		}
		for (int j = 1; j <= scc; j++)
			if (f[now][j-1] && j != now)
				dinic.add_edge(now, j+scc, 1);
	}
	for (int i = 1; i <= n; i++)
		f[i].reset();
	printf("%d
", scc - dinic.maxflow(S, T));
}

void build_DAG()
{	DAG::init();
	for (int i = 1; i <= n; i++)
	{	for (int j = Graph::head[i]; ~j; j = Graph::nxt[j])
		{	edge & e = Graph::edges[j];
			if (num[i] != num[e.to])
				DAG::add_edge(num[i], num[e.to]),
				in[num[e.to]]++,
				out[num[i]]++;
		}
	}
}

void clear()
{	memset(boo1, 0, sizeof(boo1));
	memset(boo2, 0, sizeof(boo2));
	memset(bo1, 0, sizeof(bo1));
	memset(bo2, 0, sizeof(bo2));
	memset(num, 0, sizeof(num));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	dfs_clock = 0;
	scc = 0;
	while (!st.empty()) st.pop();
}

int main()
{	int T;
	scanf("%d", &T);
	while (T--)
	{	int a, b;
		scanf("%d %d %d %d", &n, &m, &a, &b);
		clear();
		for (int i = 1; i <= a; i++)
		{	int x;
			scanf("%d", &x);
			boo1[x] = 1;
		}
		for (int i = 1; i <= b; i++)
		{	int x;
			scanf("%d", &x);
			boo2[x] = 1;
		}
		Ans = 0;
		Graph::init();
		for (int i = 1; i <= m; i++)
		{	int x, y;
			scanf("%d %d", &x, &y);
			Graph::add_edge(x, y);
		}
		for (int i = 1; i <= n; i++)
			if (!dfn[i]) Tarjan(i);
		build_DAG();
		solve();
	}
	return 0;
}

做法二:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

#include <queue>
#include <stack>

using namespace std;


const int N = 1010, M = 10010;

const int INF = 0x3F3F3F3F;


struct edge
{
	int from, to, flow, cap, dis;
	edge() { }
	edge(int _1, int _2, int _3, int _4, int _5) : from(_1), to(_2), flow(_3), cap(_4), dis(_5) { }
};


namespace Graph
{
	edge edges[2*M];
	int head[N], nxt[2*M], tot;
	
	inline void init()
	{
		memset(head, -1, sizeof(head));
		tot = 0;
	}

	inline void add_edge(int x, int y)
	{
		edges[tot] = edge(x, y, 0, 0, 0);
		nxt[tot] = head[x];
		head[x] = tot++;
	}
}


struct MCMF
{
	edge edges[2 * N * N];
	int head[2*N], nxt[2 * N * N], tot;

	inline void init()
	{
		memset(head, -1, sizeof(head));
		tot = 0;
	}

	inline void add_edge(int x, int y, int z, int k)
	{
		edges[tot] = edge(x, y, 0, z, k);
		nxt[tot] = head[x];
		head[x] = tot++;
		edges[tot] = edge(y, x, 0, 0, -k);
		nxt[tot] = head[y];
		head[y] = tot++;
	}

	int s, t;

	int L, R;


	int dist[2*N]; bool inq[2*N];

	int a[2*N], p[2*N];

	bool SPFA()
	{
		for (int i = L; i <= R; i++)
			dist[i] = -INF, inq[i] = 0;
		
		queue <int> q;
		q.push(s);
		inq[s] = 1;
		dist[s] = 0;
		a[s] = INF;
		p[s] = 0;
		
		while (!q.empty())
		{
			int x = q.front(); q.pop();
			inq[x] = 0;
			
			for (int i = head[x]; ~i; i = nxt[i])
			{
				edge & e = edges[i];
				if (e.cap > e.flow && dist[e.to] < dist[x] + e.dis)
				{
					dist[e.to] = dist[x] + e.dis;
					a[e.to] = min(a[x], e.cap - e.flow);
					p[e.to] = i;
					if (!inq[e.to]) q.push(e.to), inq[e.to] = 1;
				}
			}
		}
		
		if (dist[t] <= 0) return 0;
		
		for (int i = t; i != s; i = edges[p[i]].from)
		{
			edges[p[i]].flow += a[t];
			edges[p[i]^1].flow -= a[t];
		}
		
		return 1;
	}

	int calc(int _s, int _t)
	{
		int Ans = 0;
		s = _s, t = _t;
		while (SPFA()) Ans++;
		return Ans;
	}
} maxcostminflow;


int n, m, a, b;

bool type1[N], type2[N];


bool bo1[N], bo2[N];

int low[N], dfn[N], dfs_clock;
int bel[N], cnt;
stack <int> stk;

void Tarjan(int x)
{
	using namespace Graph;
	
	low[x] = dfn[x] = ++dfs_clock;
	stk.push(x);
	
	for (int i = head[x]; ~i; i = nxt[i])
	{
		edge & e = edges[i];
		
		if (!dfn[e.to])
		{
			Tarjan(e.to);
			low[x] = min(low[x], low[e.to]);
		}
		else if (!bel[e.to]) low[x] = min(low[x], dfn[e.to]);
	}
	
	if (low[x] == dfn[x])
	{
		int v = 0;
		cnt++;
		do
		{
			v = stk.top(); stk.pop();
			bel[v] = cnt;
			if (type1[v]) bo1[cnt] = 1;
			if (type2[v]) bo2[cnt] = 1;
		}
		while (v != x);
	}
}


bool g[N][N];

int deg1[N], deg2[N];

void solve()
{
	memset(dfn, 0, sizeof(dfn));
	memset(bel, 0, sizeof(bel));
	memset(g, 0, sizeof(g));
	memset(bo1, 0, sizeof(bo1));
	memset(bo2, 0, sizeof(bo2));
	
	cnt = 0;
	dfs_clock = 0;
	
	for (int i = 1; i <= n; i++)
		if (!dfn[i]) Tarjan(i);
	
	for (int i = 0; i < Graph::tot; i++)
	{
		edge & e = Graph::edges[i];
		if (bel[e.from] != bel[e.to]) g[bel[e.from]][bel[e.to]] = 1;
	}
	
	memset(deg1, 0, sizeof(deg1));
	memset(deg2, 0, sizeof(deg2));
	
	for (int i = 1; i <= cnt; i++)
		for (int j = 1; j <= cnt; j++)
			if (i != j && g[i][j])
				deg2[i]++, deg1[j]++;
	
	for (int i = 1; i <= cnt; i++)
		if (!deg1[i] && !bo1[i]) return void(puts("no solution"));
	for (int i = 1; i <= cnt; i++)
		if (!deg2[i] && !bo2[i]) return void(puts("no solution"));
	
	maxcostminflow.init();
	
	maxcostminflow.L = 0, maxcostminflow.R = 2 * cnt + 2;	
	
	int S = cnt + cnt + 1, T = cnt + cnt + 2;
	
	for (int i = 1; i <= cnt; i++)
		maxcostminflow.add_edge(S, i, INF, 0);
	for (int i = 1; i <= cnt; i++)
		maxcostminflow.add_edge(i+cnt, T, INF, 0);
	
	for (int i = 1; i <= cnt; i++)
	{
		maxcostminflow.add_edge(i, i+cnt, 1, 1);
		maxcostminflow.add_edge(i, i+cnt, INF, 0);
	}
	
	for (int i = 1; i <= cnt; i++)
		for (int j = 1; j <= n; j++)
			if (g[i][j])
				maxcostminflow.add_edge(i+cnt, j, INF, 0);
	
	printf("%d
", maxcostminflow.calc(S, T));
}


int main()
{
	int T;
	scanf("%d", &T);
	
	while (T--)
	{
		memset(type1, 0, sizeof(type1));
		memset(type2, 0, sizeof(type2));
		
		scanf("%d %d %d %d", &n, &m, &a, &b);
		
		Graph::init();
		
		for (int i = 1; i <= a; i++)
		{
			int x;
			scanf("%d", &x);
			type1[x] = 1;
		}
		
		for (int i = 1; i <= b; i++)
		{
			int x;
			scanf("%d", &x);
			type2[x] = 1;
		}
		
		for (int i = 1; i <= m; i++)
		{
			int x, y;
			scanf("%d %d", &x, &y);
			Graph::add_edge(x, y);
		}
		
		solve();
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/2016gdgzoi509/p/10632085.html