题意:
给你一幅有向图,你每次可以从任意点出发。图中的每条边至少要经过一次,问你至少要走几次。
建图:
设每个点i的入度减去出度为d[i], S为源点,T为汇点。
对于d[i] > 0的点i, 连边<i,T>
对于d[i] < 0的点i, 连边<S,i>
其它边连法与输入的边相同。
问题:
对于输入的每条边下界为1,我们要求的是最小值,所以问题可以转化为求该图的最小流,
显然,对于没有下界的网络 其最小流就为零, 但该题的图有下界,那么如何求有下界的网络的最小流呢?
只有下界的最小流的解法:
先判断该网络是否存在可行流,对于本题,显然存在可行流。
如果可行流存在(假设可行流的值为f1),则从可行流出发,倒向求解。
即保持网络中的弧方向不变,将T作为源点,S作为汇点,反向流一次最大流后(假设求出的最大流为f2),
那么原图的最小流为f1 - f2。
输出路径:
求完该图的最小流以后,假如边<i,j>流了c流量,那么说明边<i,j>需要走c+1次。
对于以上的情况,新建一幅要求路径的图。
你可以建c+1条边<i,j>, 也可以加一条边<i,j>和一个信息(访问次数c+1)。
我用了后者的作法,但在理论上我们把它看成c+1条边。
我们可以计算出这幅图每个点的d[i](入度减去出度)。
对于每个d[i]点,多次dfs搜索路径,每次搜一条路,并把这条路标记掉(下次不访问),并且更新这条路径所经过的点的d[i]值(其实就是,起点的出度-1, 终点的入度-1,其它点不变), 这里要注意有可能没有边可以走,那么只能搜到一个点,这种情况不用输出也不用更新d[i]
每次搜到一条路就停止dfs,然后输出路径。
代码:
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #include <vector>
- using namespace std;
- const int maxn = 110;
- const int inf = 1e9;
- #define PB push_back
- #define MP make_pair
- #define PII pair <int, int>
- #define X first
- #define Y second
- struct Edge {
- int v, c, next;
- Edge() {}
- Edge(int v, int c, int next) : v(v), c(c), next(next) {}
- } edge[maxn * maxn << 1];
- int head[maxn], E;
- int n, m;
- int S, T;
- void init() {
- E = 0;
- memset(head, -1, sizeof(head));
- }
- void add(int s, int t, int c) {
- edge[E] = Edge(t, c, head[s]);
- head[s] = E++;
- edge[E] = Edge(s, 0, head[t]);
- head[t] = E++;
- }
- int d[maxn];
- vector<PII> edges[maxn];
- int gap[maxn], dis[maxn], pre[maxn], cur[maxn];
- int sap(int s, int t, int n) {
- int i;
- for (i = 0; i <= n; i++) {
- cur[i] = head[i];
- gap[i] = dis[i] = 0;
- }
- gap[0] = n;
- int u = pre[s] = s, aug = inf, maxf = 0, v;
- while (dis[s] < n) {
- loop: for (i = cur[u]; ~i; i = edge[i].next) {
- v = edge[i].v;
- if (edge[i].c && dis[u] == dis[v] + 1) {
- aug = min(aug, edge[i].c);
- pre[v] = u;
- cur[u] = i;
- u = v;
- if (u == t) {
- while (u != s) {
- u = pre[u];
- edge[cur[u]].c -= aug;
- edge[cur[u] ^ 1].c += aug;
- }
- maxf += aug;
- aug = inf;
- }
- goto loop;
- }
- }
- int d = n;
- for (i = head[u]; ~i; i = edge[i].next) {
- v = edge[i].v;
- if (edge[i].c && d > dis[v]) {
- d = dis[v];
- cur[u] = i;
- }
- }
- if (!(--gap[dis[u]]))
- break;
- ++gap[dis[u] = d + 1];
- u = pre[u];
- }
- return maxf;
- }
- bool flag;
- int res[maxn], t;
- int ans, cnt;
- void dfs(int u) {
- int i;
- bool g = 0;
- res[t++] = u;
- for (i = 0; i < (int) edges[u].size(); i++) {
- int v = edges[u][i].X;
- int &vis = edges[u][i].Y;
- if (flag) return;
- if (!vis) continue;
- g = 1; cnt++; vis--;
- dfs(v);
- }
- if (!g) d[u]--, flag = 1;
- }
- int main() {
- int i, j, k, x;
- while (~scanf("%d", &n)) {
- memset(d, 0, sizeof(d));
- ans = 0;
- init();
- for (i = 0; i < n; i++) {
- scanf("%d", &k);
- while (k--) {
- scanf("%d", &x);
- d[--x]++;
- d[i]--;
- add(i, x, inf);
- }
- }
- S = n; T = n + 1;
- for (i = 0; i < n; i++)
- if (d[i] < 0)
- add(i, T, -d[i]), ans -= d[i];
- else if (d[i] > 0)
- add(S, i, d[i]);
- ans -= sap(S, T, T + 1);
- printf("%d\n", ans);
- for (i = 0; i < n; i++)
- edges[i].clear();
- for (i = 0; i < n; i++)
- for (j = head[i]; ~j; j = edge[j].next)
- if (!(j & 1)) {
- int v = edge[j].v;
- if (v >= n)
- continue;
- edges[i].PB(MP(v, edge[j ^ 1].c + 1));
- d[i] -= edge[j ^ 1].c;
- d[v] += edge[j ^ 1].c;
- }
- for (i = 0; i < n; i++)
- while (d[i] < 0) {
- t = 0;
- cnt = 0;
- flag = 0;
- d[i]++;
- dfs(i);
- for (j = 0; j < t; j++) {
- printf("%d", res[j] + 1);
- if (j == t - 1)
- puts("");
- else
- printf(" ");
- }
- }
- }
- return 0;
- }