• 【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;
    }
    
  • 相关阅读:
    六白话经典算法系列 高速分拣 高速GET
    neu1458 方格取数 dp解法
    自然语言处理---新词发现---微博数据预处理2
    JQuery之初探
    TFS(Team Foundation Server)介绍和入门
    ZooKeeper的学习与应用
    软考之路(六)---数据库---深入浅出 三层模式两级映像
    Open the Lock
    C/C++产生随机数
    RPM安装包-Spec文件參数具体解释与演示样例分析
  • 原文地址:https://www.cnblogs.com/2016gdgzoi509/p/10632085.html
Copyright © 2020-2023  润新知