UVA1440 有下界的最小流

题意很简单:

给出一张有向图,每次你可以从图中的任意一点出发,经过若干条边后停止,然后问你最少走几次可以将图中的每条边都走过至少一次,并且要输出方案

这个转化为网络流的话,就相当于 求一个最小流,并且存在下界,即每条边至少走一次

这让我联想到很久之前的一道题,也是有向图,问走多少条路径可以将整个图中的每条边都走过,但是跟本题不同的是,那题是不允许重复走边的。

那道题目的解是这样的:

对于图中的每个点i,设D[i]为(i的入度-i的出度)的值,按照D[i]将图中的点分类:D[i]<0的称为“入少出多”的点,D[i]>0的称为“出少入多”的点,D[i]=0的称为“入出相等”的点。则有:

定理 有向无环图中最小边路径覆盖的值等于图中所有“入少出多”的点的D值之和。

证明:

其实只需证明:对于一个至少有一条边的有向无环图,必然存在一条路径,其起点是“入少出多”的点,终点是“出少入多”的点,所有中间点都是“入出相等”的点(只要不断的在图中找到并删去这条路径,直到图中无边为止)。

首先,由于图中无环,一定存在“入少出多”的点和“出少入多”的点。

然后,假设图中所有“入少出多”的点的后继(注意:后继和后代是不同的,一个点的后代包括这个点的所有后继、所有后继的后继、所有后继的后继的后继……)也都是“入少出多”的点,则图中所有“入少出多”的点构成了一个闭合子图,在这个闭合子图中,由于所有的点都是“入少出多”的,整个子图的入度之和必然大于出度之和,这是不可能的(有向图中的所有点入度之和必然等于所有点出度之和),故可得:图中必然存在至少一个“入少出多”的点,它有不是“入少出多”的后继。

这样,在这些拥有不是“入少出多”的后继的点中选择一个点i,将其作为路径的起点,在它的不是“入少出多”的后继中选出一个点j,若j是“出少入多”的点,则边<i, j>就是符合条件的一条路径,结束;若这样的j都是“入出相等”的点,则考虑j的后代:j的后继可能都是“入少出多”的,但j的后代中必然存在至少一个点j‘不是“入少出多”的(否则j的所有后代也将构成全都是“入少出多”的闭合子图),这些j‘中必然存在一个点的前趋i‘是“入少出多”的,这是,需要舍弃原来的路径,将i‘作为新路径的起点,j‘作为新路径的第一个中间点,继续找;若j的后继不全是“入少出多”的,则若其中有“出少入多”的则路径已找到,若都是“入出相等”的,由于图中无环,将路径不断延伸,最终一定会找到合法路径。

为啥讲这道题呢,肯定跟本题有关系。

上面这个定理求出的解,肯定适用于本题。但是不一定是最优解,也就是有可能多走了若干次。

这个叫可行流,可以到对应一个建好的图中,如下所示:

设每个点i的入度减去出度为d[i], S为源点,T为汇点。

对于d[i] > 0的点i, 连边<i,T>

对于d[i] < 0的点i, 连边<S,i>

其它边连法与输入的边相同。

建好的这个图的最大流就是上边那个定理求出的解,假设为flow1。

然后有一个结论,求有下界的最小流的求法是:

首先要有可行流,然后倒向求解。

保持那个中间那些边方向不变,就是输入的那些边,

然后原来从源点连过来的点都连去汇点,原来连去汇点的都从源点连过去。

即对于d[i] < 0的点i, 连边<i,T>

对于d[i] >0的点i, 连边<S,i>

这其实啊 ,就是原来那个图,然后从T到S做最大流

假设这个求出的流量是flow2,则有下界的最小流的最优解是flow1-flow2

那么这是啥原理呢。

最开始的定理中说道对于一个至少有一条边的有向无环图,必然存在一条路径,其起点是“入少出多”的点,终点是“出少入多”的点,所有中间点都是“入出相等”的点

那么其实我们找的每条路径都是起点入少出多,差为1,终点,出少入多,也是差为1

反向建图求的是什么呢,就是在新图中起点出少入多的,差为1,终点,入少出多,也是差为1的路径

然后对于每找到这样的一个流的路径,可以将原始建图中的两条路径合并,然后新路径的中间点正好都是出入度相等的,两头出入度不同相差为1

我们通过画图来验证这个

例如此图,从上到下从左到右编号1->6

按照最开始那个定理求出的解有3个路径

1->3   2->3->4->5   4->6

然后你倒着建图 求出来了一条可以做衔接的路径 是3->4

1->3   4->6就被连接了

从而答案减去1

在画图的过程中,我们可以发现,对于每条这样的可以当做中间衔接的路径,一定可以合并原始建图中的两个路径。

然后本题的第一个问题就解决了

然后就是输出。

没有要求字典序。

具体的做法可以这样,在逆向的建图中,假如某条边走流量走了x ,则说明整个过程中该边被走了x+1次,

那么可以构造新图,同样的位置加入一条容量为x+1的边,

可以计算出这个新图的所有的d[i],对于此图,就可以使用第一个定理来做了,必然得到最优解,

在具体实现中,可以通过dfs来寻找路径,以某点为开始时,能走就走,一直走到不能走为止,注意可能从某个点发出多个路径,

并且在走的时候需要注意的是,由于我们要删掉这条路径,会导致路径的开头和结尾的d发生变化,需要作出修改

代码参考了一下网上的一个人http://blog.csdn.net/auto_ac/article/details/9398657

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <set>
#include <vector>
#include <map>
#define MAXN 111
#define MAXM 55555
#define INF 1000000007
using namespace std;
struct EDGE {
    int v, next;
    int w;
}edge[MAXM];
int head[MAXN], e;
void init() {
    memset(head, -1, sizeof(head));
    e = 0;
}
void add(int u, int v, int w) {
    edge[e].v = v;
    edge[e].w = w;
    edge[e].next = head[u];
    head[u] = e++;
    edge[e].v = u;
    edge[e].w = 0;
    edge[e].next = head[v];
    head[v] = e++;
}
int n, nt;
int h[MAXN];
int gap[MAXN];
int src, des;
int dfs(int pos, int cost) {
    if(pos == des) return cost;
    int j, minh = n - 1;
    int lv = cost, d;
    for(j = head[pos]; j != -1; j = edge[j].next) {
        int v = edge[j].v;
        int w = edge[j].w;
        if(w > 0) {
            if(h[v] + 1 == h[pos]) {
                if(lv < edge[j].w) d = lv;
                else d = edge[j].w;
                d = dfs(v, d);
                edge[j].w -= d;
                edge[j ^ 1].w += d;
                lv -= d;
                if(h[src] >= n) return cost - lv;
                if(lv == 0) break;
            }
            if(h[v] < minh) minh = h[v];
        }
    }
    if(lv == cost) {
        --gap[h[pos]];
        if(gap[h[pos]] == 0) h[src] = n;
        h[pos] = minh + 1;
        ++gap[h[pos]];
    }
    return cost - lv;
}
int sap() {
    int ret = 0;
    memset(gap, 0, sizeof(gap));
    memset(h, 0, sizeof(h));
    gap[0] = n;
    while(h[src] < n) ret += dfs(src, INF);
    return ret;
}
int d[MAXN];
typedef pair<int, int> PII;
vector<PII> g[MAXN];
int ans[MAXN];
int cnt, flag;
void dfs(int u) {
    int f = 0;
    ans[cnt++] = u;
    for(int i = 0; i < g[u].size(); i++) {
        if(flag) return;
        int v = g[u][i].first;
        if(!g[u][i].second) continue;
        f = 1;
        --g[u][i].second;
        dfs(v);
    }
    if(!f) d[u]--, flag = 1;
}
int main()
{
    int u, v, t;
    while(scanf("%d", &nt) != EOF) {
        init();
        memset(d, 0, sizeof(d));
        for(int i = 1; i <= nt; i++) {
            scanf("%d", &t);
            while(t--) {
                scanf("%d", &v);
                d[i]--;
                d[v]++;
                add(i, v, INF);
            }
        }
        src = nt + 1;
        des = nt + 2;
        n = nt + 2;
        int res = 0;
        for(int i = 1; i <= nt; i++) {
            if(d[i] > 0) add(src, i, d[i]);
            else if(d[i] < 0) add(i, des, -d[i]), res -= d[i];
        }
        printf("%d\n", res - sap());
        for(int i = 1; i <= nt; i++) g[i].clear();
        for(int i = 1; i <= nt; i++) {
            for(int j = head[i]; j != -1; j = edge[j].next) {
                if((j & 1) || edge[j].v > nt) continue;
                g[i].push_back(make_pair(edge[j].v, edge[j ^ 1].w + 1));
                d[i] -= edge[j ^ 1].w;
                d[edge[j].v] += edge[j ^ 1].w;
            }
        }
        for(int i = 1; i <= nt; i++) {
            while(d[i] < 0) {
                cnt = 0;
                flag = 0;
                d[i]++;
                dfs(i);
                for(int j = 0; j < cnt; j++) {
                    printf("%d", ans[j]);
                    if(j == cnt - 1) printf("\n");
                    else printf(" ");
                }
            }
        }
    }
	return 0;
}
时间: 2024-10-05 04:27:34

UVA1440 有下界的最小流的相关文章

【BZOJ-2502】清理雪道 有上下界的网络流(有下界的最小流)

2502: 清理雪道 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 594  Solved: 318[Submit][Status][Discuss] Description 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部.从降落的地点出

[BZOJ2502]清理雪道解题报告|带下界的最小流

滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定时清理雪道.你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部.从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道. 由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务. 题意就是给定一张DAG,求每条边起码经过一次求覆盖所有边的最小路径条数

POJ 3801/HDU 3157 Crazy Circuits | 有下界的最小流

题目: POJ最近总是炸 所以还是用HDU吧http://acm.hdu.edu.cn/showproblem.php?pid=3157 题解: 题很长,但其实就是给个有源汇带下界网络流(+是源,-是汇),求最小流 求法: 1.模仿可行流建图,但是不加t到s的INF边 2.跑最大流 3.加t到sINF边 4.跑最大流 5.如果两次答案相加不等于sum,无解; 6.如果有解,t到s的反边流量就是答案 #include<cstdio> #include<algorithm> #incl

HDU 3157 Crazy Circuits(有源汇上下界最小流)

HDU 3157 Crazy Circuits 题目链接 题意:一个电路板,上面有N个接线柱(标号1~N),还有两个电源接线柱 + -,给出一些线路,每个线路有一个下限值求一个可以让所有部件正常工作的总电流 没有则输出impossible 思路: 有源汇有上下界求最小流,建模方法为: 按无源汇先建图,跑超级源汇ss->tt一次,然后加入t->s,容量INF的边,在跑一次ss->tt,如果是满流,就有解,解为t->s边的当前流量 顺带写个最大流的,最大流就先把t->s加入直接跑

POJ 3801 有上下界最小流

1: /** 2: POJ 3801 有上下界的最小流 3: 4: 1.对supersrc到supersink 求一次最大流,记为f1.(在有源汇的情况下,先使整个网络趋向必须边尽量满足的情况) 5: 2.添加一条边sink -> src,流量上限为INF,这条边记为p.(构造无源汇网络) 6: 3.对supersrc到supersink再次求最大流,记为f2,这里判断是否为可行流.(要判断可行,必须先构造无源汇网络流,因此要再次求最大流) 7: 8: 此网络流的最小流即为 sink -> s

Flow construction SGU - 176 有源汇有上下界最小流 二分法和回流法

/** 题目:Flow construction SGU - 176 链接:https://vjudge.net/problem/SGU-176 题意: 有源汇有上下界的最小流. 给定n个点,m个管道.每个管道给出u,v,z,c.u表示起点,v表示终点,z表示容量,如果c==1,那么表示还有下界为z. 如果c==0,表示没有下界. 求从1到n的最小流. 思路: 第一种做法: 转化为无源汇求超级源S到超级汇T的最大流flow1(此时从s出发的流和为flow1),然后讲t到s的边删掉(可以使流量等于

有上下界的最大流解法

问题模型: 给定一个加权的有向图,满足: (1)容量限制条件: (2)流量平衡条件: (2)中的即除了源汇外,所有点都满足流量平衡条件,则称G为有源汇网络:否则,即不存在源汇,所有点都满足流量平衡条件,则称G为无源汇网络. 将这类问题由易到难一一解决: 问题[1] 求无源汇的网络有上下界的可行流 由于下界是一条弧上的流必需要满足的确定值.下面引入必要弧的概念:必要弧是一定流要满的弧.必要弧的构造,将容量下界的限制分离开了,从而构造了一个没有下界的网络G': 1. 将原弧(u,v)分离出一条必要弧

【BZOJ3876】【Ahoi2014】支线剧情 有下界的最小费用最大流

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43025375"); } [BZOJ2324]营救皮卡丘 这道题也是一道有下界的最小费用最大流. 我的题解地址:http://blog.csdn.net/vmurder/article/details/41378979 这道题其实就是模板题. 我的处理

有上下界的网络流3-有源汇带上下界最小流SGU176

题目大意:有一个类似于工业加工生产的机器,起点为1终点为n,中间生产环节有货物加工数量限制,输入u v z c, 当c等于1时表示这个加工的环节必须对纽带上的货物全部加工(即上下界都为z),c等于0表示加工上界限制为z,下界为0,求节点1(起点)最少需要投放多少货物才能传送带正常工作. 解题思路:    1.直接 增设超级源点ss和超级汇点tt并连上附加边,对 当前图 求 无源汇带上下界可行流    2.将图的汇点sd连一条容量无限制的边到图的源点st,再求一遍 无源汇带上下界可行流    3.