線性規劃與網路流 03
最小路徑覆蓋問題
題意:有向無環圖,如何以最少的路徑數覆蓋圖中所有點,要求每個點在且僅在一條路徑上。
思路:定理:乙個有向無環圖的最小路徑覆蓋等於最大匹配。
也就是說,求出最大匹配後,用總點數減去最大匹配值即能得到最小路徑數。
如何用網路流跑二分匹配?把原來的點拆成兩個點,命名為x、y
。 即->u。
然後設源點s
,向每個
x連容量為
1的邊。
設匯點t
,每個y向t
連容量為
1的邊。
若某個原始點i與j
有有向邊(i,
j),則轉化成(
xi,yj)。
然後跑一遍最大流,輸出方案時dfs
一下即可,詳見程式。
原始碼:#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define inf (1000000000)
const int maxn = 1000 + 5;
int head[maxn], cur[maxn], cnt;
int n, m;
int d[maxn], p[maxn], num[maxn];
queueque;
struct edge
int u, v;
int flow, ne;
edge(){}
edge(int _u, int _v, int _flow)
}edge[maxn * maxn * 2];
void add_edge(int u, int v)
edge[cnt] = edge(u, v, 1);
head[u] = cnt++;
edge[cnt] = edge(v, u, 0);
head[v] = cnt++;
void init()
cnt = 0;
memset(head, -1, sizeof(head));
int u, v;
for(int i = 1 ; i <= n ; i++)
add_edge(0, i * 2), add_edge(i * 2 + 1, 1);
for(int i = 0 ; i < m ; i++){
scanf("%d%d", &u, &v);
add_edge(u * 2, v * 2 + 1);
int vis[maxn];
void bfs(int t)
memset(vis, 0, sizeof(vis));
while(!que.empty()) que.pop();
d[t] = 0;
vis[t] = 1;
que.push(t);
while(!que.empty()){
int u = que.front(); que.pop();
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(vis[v] == 0){
vis[v] = 1;
que.push(v);
d[v] = d[u] + 1;
int augment(int s, int t)
int ans = inf;
int now = t;
while(now != s){
ans = min(ans, edge[p[now]].flow);
now = edge[p[now]].u;
now = t;
while(now != s){
edge[p[now]].flow -= ans;
edge[p[now]^1].flow += ans;
now = edge[p[now]].u;
return ans;
int isap(int s, int t)
int flow = 0;
bfs(t);
memset(num, 0, sizeof(num));
for(int i = 0 ; i <= n * 2 ; i++)
cur[i] = head[i], num[d[i]]++;
while(!que.empty()) que.pop();
int u = s;
while(d[s] < n * 2 + 1){
if(u == t){
flow += augment(s, t);
u = s;
int ok = 0;
for(int now = cur[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(edge[now].flow && d[v] == d[u] - 1){
p[v] = now;
cur[u] = now;
u = v;
ok = 1;
break;
if(!ok){
int tm = n * 2 + 1;
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
if(edge[now].flow) tm = min(tm, d[edge[now].v]);
if(--num[d[u]] == 0) break;
num[d[u] = tm + 1]++;
cur[u] = head[u];
if(u != s) u = edge[p[u]].u;
return flow;
int out[maxn], out_cnt;
void dfs(int u)
out[out_cnt++] = u;
vis[u] = 1;
u = 2 * u;
for(int now = head[u] ; now != -1 ; now = edge[now].ne){
int v = edge[now].v;
if(vis[v / 2] == 0 && edge[now].flow == 0 && v != 0){
dfs(v / 2);
int main()
freopen("path10.in", "r", stdin);
while(scanf("%d%d", &n, &m) != eof){
init();
int ans = n - isap(0, 1);
memset(vis, 0, sizeof(vis));
for(int i = 1 ; i <= n ; i++){
if(vis[i] == 0){
out_cnt = 0;
dfs(i);
int f = 1;
for(int j = 0 ; j < out_cnt ; j++){
if(f) f = 0;
else printf(" ");
printf("%d", out[j]);
printf("\n");
printf("%d\n", ans);
return 0;
網路流最小路徑覆蓋
思路 每個點x拆成兩個點x和x 分別表示x作為前驅和作為後繼。若原圖中x和y有邊,向x和y 加一條有向邊。如此構成二分圖,記此二分圖中作為前驅的節點集合為a,作為後繼的節點集合為b。跑最大匹配,沒有匹配的點的個數 n 最大匹配數 就是需要的最少的路徑條數。正確性 二分匹配可以保證每個點頂多只有乙個前...
線性規劃網路流問題總結
線性規劃可見單純形演算法 網路流 增廣路演算法主體 具體增廣路增流方案 演算法步驟 isap演算法 即貼標籤,對所有的頂點標記到匯點的最短距離 最開始從匯點開始,匯點a的層數記錄為0 遍歷與a相鄰的點的層數,計算公式為在a的層數基礎上加一即可 重複第二步驟,直到到大源點 然後尋找增廣路徑,按照 fl...
學習筆記 線性規劃 網路流
max min 來道例題 洛谷p3337 zjoi2013 防守戰線 列出式子 begin mathrm sum i c i y i s.t.forall i in 1,n sum y j geq d i forall i in 1,n y i geq 0 end 令 a intercal j in...