UVA1440 有下界的最小流
来源:互联网 发布:matlab矩阵小数化整数 编辑:程序博客网 时间:2024/06/05 04:18
题意很简单:
给出一张有向图,每次你可以从图中的任意一点出发,经过若干条边后停止,然后问你最少走几次可以将图中的每条边都走过至少一次,并且要输出方案
这个转化为网络流的话,就相当于 求一个最小流,并且存在下界,即每条边至少走一次
这让我联想到很久之前的一道题,也是有向图,问走多少条路径可以将整个图中的每条边都走过,但是跟本题不同的是,那题是不允许重复走边的。
那道题目的解是这样的:
对于图中的每个点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 1000000007using 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;}
- UVA1440 有下界的最小流
- hust 1342 (有上下界的最小流)
- hdu 3157(有上下界的最小流)
- hust1342(流量有上下界的最小流)
- HDU - 3157 Crazy Circuits(有下界的最小流)
- bzoj 2502(有上下界的最小流)
- bzoj 3876(有上下界的最小费用流)
- 【BZOJ2502】清理雪道【有上下界的最小流】
- [BZOJ2502]清理雪道 有上下界的最小流
- [BZOJ3876][AHOI2014]支线剧情-有下界的最小费用流
- HUST 1342 有上下界最小流
- sgu 176 Flow construction (有汇源有上下界的最小流)
- 有上下界的流
- 流量有上下界的网络的最大流和最小流算法
- 网络流(最大流、最小费用最大流、有上下界的网络流)
- 2502: 清理雪道|有上下界的网络流之最小流
- 最小矩阵差——有上下界的网络流问题
- SGU 176 Flow construction 有源汇 有上下界的最小流
- MySQL数据类型和java数据类型
- 给大家讲解。数据结构篇(一) 动态数组ngx_array_t
- openssl提取pfx证书密钥对
- 虾米音乐Qt版下载器之Location解密部分
- Visual Studio下SQLite数据库开发环境设置
- UVA1440 有下界的最小流
- ALTER DATABASE 数据库镜像 (Transact-SQL)-转
- android开发之关于彻底退出程序
- Array类和String类
- openssl证书格式转换
- 线程的挂起与唤醒之实现app加载广告点击跳转功能
- 状态栏显示相关View Flag参数介绍
- %1$s %1$d Android string
- 在UI线程中实现定时隐藏UI控件的效果