题目

传送门


题解

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

不可相交最小路径覆盖

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

可用最小割解决。
考虑把原图中的每一个点看成一条单独的路径, 接着不断将路径合并, 形成更长的路径。
那么最后的路径数 = 总点数 - 合并次数\((\)没合并次减少一条路径\()\), 要使路径数最小就要让合并次数最多。
可用二分图匹配计算。
把原图中的一个点\(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\n", 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\n", 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

【BZOJ2893】征服王相关推荐

  1. BZOJ2893:征服王(费用流)

    Description 虽然春希将信息传递给了雪菜,但是雪菜却好像完全不认得春希了.心急如焚的春希打开了第二世代机能,对雪菜的脑内芯片进行了直连-hack. 进入到雪菜内部的春希发现(这什么玩意..) ...

  2. UOJ #2893. 征服王

    题意:起点和终点有限制的最小可重复路径覆盖. 思路1:上下界网络流,拆点,使得每个点的流量下界限制为1.(注意有源汇上下界网络流只能跑DAG) #include <bits/stdc++.h&g ...

  3. 王凯丽王俊凯到底是什么关系?

    人们最近总说王凯丽如何如何美,但知道他是男扮女装的吗?呵呵,其实这些和王俊凯挺有关系,那么王凯丽是谁?王凯丽指王俊凯,英文名karry,因王俊凯微博晒女装照引发网友热议.王俊凯发了条这样的微博:&qu ...

  4. linux can测试程序,Linux CAN Shell 测试脚本程序

    [BZOJ-2893]征服王 最大费用最大流(带下界最小流) 2893: 征服王 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 156  Solved ...

  5. 鸿蒙dnf怎么样,DNF:最保值时装终于再出,当年卖300如今值5000,又是全服真香?...

    DNF最稀有的时装是什么? 首先肯定不是最新推出的龙袍,也不是历代"绝版"天空,而是那些各期的联动时装.它们跟DNF能随便追忆的天空套不同,因其商业特殊性,复刻的难度相当大.其中F ...

  6. 第十七届全国大学生智能车竞赛全国总决赛参赛队伍

    根据 第十七届全国大学智能汽车竞赛全国总决赛名单 ,其中 268 支队伍参加 在8月20 - 22 日 在南京信息工程大学举办的线下总决赛,154 支队伍参加 线上总决赛 . 具体名单如下: §01 ...

  7. 幻想的时代 CS

    这是最好的时代,这是最坏的时代:这是智慧的时代,这是愚蠢的时代:这是信仰的时期,这是怀疑的时期:这是光明的季节,这是黑暗的季节:这是希望之春,这是失望之冬:人们面前有着各样事物,人们面前一无所有:人们 ...

  8. 第十七届全国大学生智能汽车竞赛全国总决赛成绩公告

    01 线下竞赛成绩 1.1 四轮电磁组 1.1.1 本科生组 编号 学校 队伍 8月21日成绩 8月22日成绩 最佳成绩 排名 获奖等级 1 杭州电子科技大学 杭电四轮电磁一队 27.915 28.6 ...

  9. 第十七届全国大学智能汽车竞赛全国总决赛名单

    简 介: 本文给出了参加第十七届全国大学能车竞赛全国总决赛队伍名单产生办法与进入总决赛的队伍名单.还有部分专项创新奖项名单以及三个综合竞赛组别决赛名单将另行公告. 关键词: 智能车竞赛,总决赛名单,1 ...

最新文章

  1. 为什么蚂蚁永远不会堵车?
  2. border-raduis 在IE8中的兼容性问题
  3. C#开发Unity游戏教程之判断语句
  4. windows mobile 鼠标等待
  5. 10分钟理解依赖注入
  6. marked Options
  7. mysql 索引及索引创建原则
  8. 模拟赛 10-14考试再次翻车记
  9. mysql事物介绍_MySQL--事务介绍
  10. [leetcode] 96. 不同的二叉搜索树 +[补充] 不同的二叉树,不同形态的二叉树的个数----catalan数
  11. Codeforces Round #415 (Div. 2) C. Do you want a date?
  12. python dll 调用 方法未找到_大牛经验分享之谈:Python调用.NET库的方法步骤(建议收藏)...
  13. 程序猿不能不知道的网站
  14. Java根据关键字在PDF/Word插入图片
  15. python 完全背包问题_完全背包问题及Python代码实现
  16. DIY,PC采购,服务器介绍
  17. Vue海报编辑器(自由拖拽海报生成)
  18. 【NIPS挑战赛优胜解】用机器学习判断基因变异所属类别
  19. 1-2 Verilog 4位 二选一 多路选择器
  20. leaflet 加载高德地图

热门文章

  1. 【ESD专题】静电防护物品、静电测试工具及防静电符号
  2. 软件测试为网络安全护航
  3. 帝都机器人便利店_飘了?帝都这几家网红便利店,竟藏着“米其林”早餐!
  4. 基于安卓端的房产网上交易平台app
  5. 【玩具】获取母校师资信息
  6. Excel表格怎么换行?4个方法任你选!
  7. Java Optional使用
  8. yaml引号用法_字符串包含空格或特殊字符_需要加引号
  9. web自动化之验证码识别解决方案
  10. 瑜伽机构如何线上、线下相结合,实现内容变现?