网络流不难写,难的建一个能解决问题的模型。。
即使我知道这是网络流专题的题目,也绝不会能想出这种解法,=_=||
题意:
给出一个矩阵的 前i行和 以及 前i列和,然后找到一个满足要求的矩阵,而且每个元素在1~20之间。
分析:
先求出每行的元素和A'i 每列的元素和B'i
紫书上说建一个二分图,每行是一个X节点,每列代表一个Y节点。
因为流量最小是0,而题中说元素大小在1~20之间,所以我们先将每个元素都减一。
这样每行的元素和就变成了A'i-C,每列之和变为B'i-R
XY之间每条边的容量为19
源点到X中每个点的容量为A'i-C,Y中的每个点到汇点的容量为B'i-R。当所有从源点出发和在汇点结束的边满载时有解。
“为什么这样做是对的呢?请读者思考。”
好吧,那我就思考。Xi->Yj这条边就对应矩阵中第i行第j列元素的值,而且所有从X出发的边,汇聚到Yj的总流量就是第j列的和。
反过来,从Xi出发的总流量就是第i行的和,分流到各个Y中。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 50 + 5; 6 const int INF = 1000000000; 7 8 struct Edge 9 { 10 int from, to, cap, flow; 11 Edge(int u=0, int v=0, int c=0, int f=0): from(u), to(v), cap(c), flow(f) {} 12 }; 13 14 struct EdmondsKarp 15 { 16 int n, m; 17 vector<Edge> edges; 18 vector<int> G[maxn]; 19 int a[maxn]; 20 int p[maxn]; 21 22 void Init(int n) 23 { 24 for(int i = 0; i < n; ++i) G[i].clear(); 25 edges.clear(); 26 } 27 28 void AddEdge(int from, int to, int cap) 29 { 30 edges.push_back(Edge(from, to, cap, 0)); 31 edges.push_back(Edge(to, from, 0, 0)); 32 m = edges.size(); 33 G[from].push_back(m-2); 34 G[to].push_back(m-1); 35 } 36 37 int MaxFlow(int s, int t) 38 { 39 int flow = 0; 40 for(;;) 41 { 42 memset(a, 0, sizeof(a)); 43 queue<int> Q; 44 Q.push(s); 45 a[s] = INF; 46 while(!Q.empty()) 47 { 48 int x = Q.front(); Q.pop(); 49 for(int i = 0; i < G[x].size(); ++i) 50 { 51 Edge& e = edges[G[x][i]]; 52 if(!a[e.to] && e.cap > e.flow) 53 { 54 a[e.to] = min(a[x], e.cap - e.flow); 55 p[e.to] = G[x][i]; 56 Q.push(e.to); 57 } 58 } 59 if(a[t]) break; 60 } 61 if(!a[t]) break; 62 for(int u = t; u != s; u = edges[p[u]].from) 63 { 64 edges[p[u]].flow += a[t]; 65 edges[p[u]^1].flow -= a[t]; 66 } 67 flow += a[t]; 68 } 69 return flow; 70 } 71 }; 72 73 EdmondsKarp g; 74 int ind[maxn][maxn];//ind[i][j]记录第i行第j列对应的边的编号 75 76 int main() 77 { 78 //freopen("in.txt", "r", stdin); 79 80 int T, R, C; 81 scanf("%d", &T); 82 for(int kase = 1; kase <= T; ++kase) 83 { 84 scanf("%d%d", &R, &C); 85 g.Init(R+C+2); 86 int cur, last = 0; 87 for(int i = 1; i <= R; ++i) 88 {//第i行的节点标号为i,源点标号为0 89 scanf("%d", &cur); 90 g.AddEdge(0, i, cur - last - C); 91 last = cur; 92 } 93 last = 0; 94 for(int i = 1; i <= C; ++i) 95 {//第i列的标号为R+i,汇点标号为R+C+1 96 scanf("%d", &cur); 97 g.AddEdge(R+i, R+C+1, cur - last - R); 98 last = cur; 99 } 100 for(int i = 1; i <= R; ++i) 101 for(int j = 1; j <= C; ++j) 102 { 103 g.AddEdge(i, j+R, 19); 104 ind[i][j] = g.edges.size() - 2;//因为AddEdge中还有一条反向边,所以是-2 105 } 106 g.MaxFlow(0, R+C+1); 107 108 printf("Matrix %d\n", kase); 109 for(int i = 1; i <= R; ++i) 110 { 111 printf("%d", g.edges[ind[i][1]].flow + 1); 112 for(int j = 2; j <= C; ++j) 113 printf(" %d", g.edges[ind[i][j]].flow + 1); 114 printf("\n"); 115 } 116 printf("\n"); 117 } 118 119 return 0; 120 }