線性規劃與網路流 03最小路徑覆蓋問題

2021-07-05 02:34:26 字數 3755 閱讀 3563

線性規劃與網路流 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...