第一次接触网络流,还不习惯用这种很大块而且不知道内部原理的“模板”,熟悉花了挺长时间,勉强算是会用了吧
对最大流,最省费用最大流的意义还需要加深理解,可以多复习一下最后一题,然后,刷题–
前两道是模板题
Flow problem
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
#define type int //type类型可以是long long,int,double
#define maxn 25 //点数
#define maxm 2005 //边数,注意大于最小边数的两倍
#define INF 1e8 //1e8,1e9都可
int n,m;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}
}edge[maxm];
int tol;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);
}
void add_edge (int u, int v, type w, type rw=0) { //模板加边
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;
}
type sap (int start, int eend, int N) { //sap求最大流
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == eend) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
int main(){
int tt; //这里的案例数注意不能用习惯的t,会隐藏全局变量
int cas=1;
scanf("%d",&tt);
while(tt--){
init(); //每次初始化
cin>>n>>m;
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
add_edge(u,v,w); //调用模板一
}
s=1,t=n; //要给全局变量s,t,n赋值
int ans=sap(1,n,n); //调用二
printf("Case %d: %d\n",cas++,ans);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=405;
const int maxm=405;
const int INF=2e9+2e6;
#define type int
int n;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}
}edge[maxm];
int tol;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);
}
void add_edge (int u, int v, type w, type rw=0) {
//cout << u << " " << v << " " << w << endl;
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;
}
type sap (int start, int end, int N) {
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == end) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
int main(){ //主体
int m;
while(~scanf("%d%d",&m,&n)){
init();
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
}
s=1,t=n;
int ans=sap(1,n,n);
printf("%d\n",ans);
}
return 0;
}
Admiral
题意: 有n个点m条边,求从1到n的不相交,且费用最小的两条路,输出最小和费用
思路: 不相交 ——> 拆除了1,n的所有点,拆出的边限流1
两条路 ——> 拆1,n点,限流2
费用最小——> 最小费用最大流
剩下的套模板就好啦~
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxm=50010;
const int maxn=2005;
const int INF=1e9;
#define type int
int s, t;
struct node {
int u, v, next;
type cap, flow, cost;
}edge[maxm];
int head[maxn], cnt;
int pre[maxn];
type dis[maxn];
bool vis[maxn];
int N;
void init () {
memset (head, -1, sizeof head);
cnt = 0;
}
void add_edge (int u, int v, type cap, type cost) {
edge[cnt].u = u, edge[cnt].v = v, edge[cnt].cap = cap, edge[cnt].flow = 0;
edge[cnt].cost = cost, edge[cnt].next = head[u], head[u] = cnt++;
edge[cnt].u = v, edge[cnt].v = u, edge[cnt].cap = 0, edge[cnt].flow = 0;
edge[cnt].cost = -cost, edge[cnt].next = head[v], head[v] = cnt++;
}
bool spfa (int s, int t) {
queue <int> q;
for (int i = 0; i <= N; i++) {
dis[i] = INF;
vis[i] = 0;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = 1;
q.push (s);
while (!q.empty ()) {
int u = q.front (); q.pop ();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if (edge[i].cap > edge[i].flow && dis[v] > dis[u]+edge[i].cost) {
dis[v] = dis[u]+edge[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = 1;
q.push (v);
}
}
}
}
if (pre[t] == -1)
return 0;
else
return 1;
}
int MCMF (int s, int t, type &cost) {
type flow = 0;
cost = 0;
while (spfa (s, t)) {
type Min = INF;
for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {
if (Min > edge[i].cap-edge[i].flow) {
Min = edge[i].cap-edge[i].flow;
}
}
for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost*Min;
}
flow += Min;
}
return flow;
}
int main(){
int m;
while(~scanf("%d%d",&N,&m)){
init();
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u+N,v,1,w); //想想为什么是u+N
}
s=1,t=2*N;
add_edge(1,1+N,2,0); //拆1,n点
add_edge(N,2*N,2,0);
for(int i=2;i<=N-1;i++){
add_edge(i,i+N,1,0); //拆其他点
}
int cost;
N=2*N; //注意全局变量N的含义,细节
int flow=MCMF(1,N,cost);
printf("%d\n",cost);
}
return 0;
}
Going Home
题意: 给一个N*M的矩阵,矩阵中m与h的个数相同,m与h之间的距离为横纵方向相差的格子数之和,输出给每个m匹配一个h后,最小的距离之和 (m可以穿过h所在的格子,N,M,h的个数都<=100)
思路: 显然的二分匹配,在每个m与h之间加边,费用为距离差
建立一超级源点,超级汇点,与每个m或h的边费用为1,套最小费用最大流的模板
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int INF=1<<29;
const int maxn=610;
const int maxm=30010;
#define type int
int s, t;
struct node {
int u, v, next;
type cap, flow, cost;
}edge[maxm];
int head[maxn], cnt;
int pre[maxn];type dis[maxn];bool vis[maxn];int N;
void init () {
memset (head, -1, sizeof head);
cnt = 0;}
void add_edge (int u, int v, type cap, type cost) {
edge[cnt].u = u, edge[cnt].v = v, edge[cnt].cap = cap, edge[cnt].flow = 0;
edge[cnt].cost = cost, edge[cnt].next = head[u], head[u] = cnt++;
edge[cnt].u = v, edge[cnt].v = u, edge[cnt].cap = 0, edge[cnt].flow = 0;
edge[cnt].cost = -cost, edge[cnt].next = head[v], head[v] = cnt++;}
bool spfa (int s, int t) {
queue <int> q;
for (int i = 0; i < N; i++) {
dis[i] = INF;
vis[i] = 0;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = 1;
q.push (s);
while (!q.empty ()) {
int u = q.front (); q.pop ();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if (edge[i].cap > edge[i].flow && dis[v] > dis[u]+edge[i].cost) {
dis[v] = dis[u]+edge[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = 1;
q.push (v);
}
}
}
}
if (pre[t] == -1)
return 0;
else
return 1;}
int MCMF (int s, int t, type &cost) {
type flow = 0;
cost = 0;
while (spfa (s, t)) {
type Min = INF;
for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {
if (Min > edge[i].cap-edge[i].flow) {
Min = edge[i].cap-edge[i].flow;
}
}
for (int i = pre[t]; i != -1; i = pre[edge[i^1].v]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost*Min;
}
flow += Min;
}
return flow;
}
struct Node{
int x,y;
};
vector<Node> man,house;
int main(){
int nn,m;
while(~scanf("%d%d",&nn,&m)&&nn){
init();
char c[110];
man.clear(); //用途理清
house.clear();
for(int i=0;i<nn;i++){
scanf("%s",c);
for(int j=0;j<m;j++)
if(c[j]=='H') house.push_back(Node{i,j});
else if(c[j]=='m') man.push_back(Node{i,j});
}
int tot=house.size();
s=0,t=2*tot+1;
N=2*tot+2; //每次总点数这边都很容易理错…… 多理几遍
for(int i=0;i<tot;i++){
add_edge(0,i+1,1,0); //给每个man连超级源点
for(int j=0;j<tot;j++)
add_edge(i+1,j+1+tot,1,abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y)); //每个man和house连边
add_edge(i+1+tot,2*tot+1,1,0); //给每个house连超级汇点
}
int cost;
MCMF(0,2*tot+1,cost); //套模板
printf("%d\n",cost);
}
return 0;
}
啊这是我WA了>20次,debug两个小时,最后还是觉得一开始代码没问题的题 ~嗷
还是网络流题目做的不熟,代码写的不够优美,姿势不太标准吧……
Delivery Bears*********
题意: 有x只熊,从带费用网络流图的点1走到n,要求每只熊背的重量相同,求n点能接收到的最大总重量(每只熊背的重量*x)
思路: 一开始想到了二分,但卡在怎么检查mid的值上,后来还是学长给了思路。
将每条路的权值除以mid,就得出每只熊背mid重量的物品时,每条路能通过的最多的熊的数量,源点和汇点拆成权为x的边,然后最大流跑一遍,判断一下是否ans==x即可
代码还是要好好理一下啊
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <algorithm>
using namespace std;
#define type int
const int INF=1<<29;
const int maxn=600;
const int maxm=20010;
int n,m;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}
}edge[maxm];
int tol;int head[maxn];int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);}
void add_edge (int u, int v, type w, type rw=0) {
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;}
type sap (int start, int eend, int N) {
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == eend) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
struct rese{
int u,v;
int w;
}res[maxm];
int x;
bool check( double mid){
init();
for(int i=0;i<m;i++)
add_edge(res[i].u,res[i].v,min((res[i].w/mid),1.0*x)); //注意min函数里面比较的类型应该一致
add_edge(0,1,x);
add_edge(n-2,n-1,x);
int ans=sap(0,n-1,n);
if(ans==x) return true;
else return false;
}
int main(){ //论如何将代码写的更加优美==
scanf("%d%d%d",&n,&m,&x);
init();
for(int i=0;i<m;i++){
scanf("%d%d%d",&res[i].u,&res[i].v,&res[i].w);
}
n+=2; //最后错原来在这里…… n是包括超级源点和汇点的总点数,之前忘了+2,check函数中也没有注意噗。。
double l=0;
double r=1e9;
double mid,ans=0;
for(int i=0;i<100;i++){ //用这样的循环会比eps更加精确
mid=(r+l)/2;
if(check(mid))
l=mid,ans=mid;
else
r=mid;
}
printf("%.10f\n",ans*x);
return 0;
}
最后两题是紫书上的原题,重点复习最后一题
A Plug of Unix
题意: 有n个设备,m个插座,k个转换器,转换器可以叠加使用且数量无限(开始没审清题意噗。。),求最少有多少个设备无法充电
思路: 建立一个超级源点与汇点,将所有的插头插座种类都加进图中
若是设备的插头,则与超级源点连一条上限为1的边
若是插座,则与超级汇点连一条上限为1的边
是转换器x->y,则在x与y之间连一条上限无穷大的有向边
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
const int INF=1e9;
const int maxn=1010;
const int maxm=10000;
#define type int
int n;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}}edge[maxm];
int tol;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);}
void add_edge (int u, int v, type w, type rw=0) {
//cout << u << " " << v << " " << w << endl;
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;}
type sap (int start, int eend, int N) {
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == eend) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
map<string,int> mark; //给不同种的插头插座编号
int ID(string name){
if(mark.count(name)) return mark[name];
else{
mark[name]=n++; //n在这里计数,理解含义
return mark[name];
}
}
int main(){
int tt;
scanf("%d",&tt);
while(tt--){
init();
mark.clear();
n=1;
int N,M;
scanf("%d",&M);
s=0,t=1000;
vector<int> vec;
for(int i=0;i<M;i++){ //设备
string name;
cin>>name;
int id=ID(name);
vec.push_back(id);
}
scanf("%d",&N);
for(int i=0;i<N;i++){ //插座
string name,plug;
cin>>name>>plug;
int id=ID(plug);
add_edge(0,id,1);
}
int m;
scanf("%d",&m);
for(int i=0;i<m;i++){ //转换器
string a,b;
cin>>a>>b;
int aa=ID(a);
int bb=ID(b);
add_edge(aa,bb,INF);
}
for(int i=0;i<vec.size();i++) //注意这个放置的顺序==,得放在转换器读完之后==
add_edge(vec[i],n,1);
int ans=sap(0,n,n+1);
printf("%d\n",N-ans);
if(tt) printf("\n");
}
return 0;
}
哈哈哈哈终于到了最后一题
Matrix decompressing***************
题意: 一个R*C数阵, 已知前i行之和R[i](1<=i<=R)和前j列之和C[j](1<=j<=C),输出一个满足条件的矩阵 (1<=R,C,矩阵中每个数<=20)
思路:
这个思路很神奇,将题目化作二分匹配来做
可以求解出每行每列的数字之和,R’[i],C’[j]
让矩阵中每个数都减一,则每一行的值减C(R’[i]-C),每一列的值减R (C’[j]-R)
建立超级源点和汇点,超级源点与代表行的点相连,上限为R’[i]-C,超级汇点与代表列的点相连,上限为C’[j]-R
代表行的点与每个代表列的点相连,上限为19
求一遍最大流,满流则有解,解为边最后的流量
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
// 好巧妙的一题 *********
#define type int
const int INF=1e9;
const int maxn=200;
const int maxm=1050;
int n;
int s, t;
struct Edge {
int from, to, next;
type cap, flow;
void get (int u, int a, int b, type c, type d) {
from = u; to = a; next = b; cap = c; flow = d;
}
}edge[maxm];
int tol;
int head[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init () {
tol = 0;
memset (head, -1, sizeof head);}
void add_edge (int u, int v, type w, type rw=0) {
//cout << u << " " << v << " " << w << endl;
edge[tol].get(u, v,head[u],w,0);head[u]=tol++;
edge[tol].get(v, u,head[v],rw,0);head[v]=tol++;}
type sap (int start, int eend, int N) {
memset (gap, 0, sizeof gap);
memset (dep, 0, sizeof dep);
memcpy (cur, head, sizeof head);
int u = start;
pre[u] = -1;
gap[0] = N;
type ans = 0;
while (dep[start] < N) {
if (u == eend) {
type Min = INF;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap-edge[i].flow;
for (int i = pre[u]; i != -1; i = pre[edge[i^1].to]) {
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if (flag) {
u = v;
continue;
}
int Min = N;
for (int i = head[u]; i != -1; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if (u != start) u = edge[pre[u]^1].to;
}
return ans;
}
int row[25],cow[25];
int main(){ //重点
int tt;scanf("%d",&tt);
int cas=1;
while(tt--){
init();
int r,c;
scanf("%d%d",&r,&c); //读入
for(int i=0;i<r;i++)
scanf("%d",&row[i]);
for(int i=0;i<c;i++)
scanf("%d",&cow[i]);
//处理出每一行每一列的和
for(int i=r-1;i>0;i--) row[i]-=row[i-1];
for(int j=c-1;j>0;j--) cow[j]-=cow[j-1];
s=0,t=r+c+1;
n=r+c+2;
//超级源点,超级汇点加边
for(int i=0;i<r;i++)
add_edge(0,i+1,row[i]-c);
for(int j=0;j<c;j++)
add_edge(r+j+1,r+c+1,cow[j]-r);
//行的点与列的点之间加边,注意加边顺序
for(int i=0;i<r;i++)
for(int j=c-1;j>=0;j--) //这里放入边的顺序会影响最后的输出顺序
add_edge(i+1,r+j+1,19);
sap(0,r+c+1,r+c+2);
printf("Matrix %d\n",cas++);
for(int i=1;i<=r;i++){
for(int j=head[i];edge[j].next!=-1;j=edge[j].next){ //head[u]存以u为起点的所以边的值,注意最后一条是到超级源点的逆序边
printf("%d",edge[j].flow+1);
if(edge[edge[j].next].next!=-1) printf(" ");
}
printf("\n");
}
}
return 0;
}
图论这才刚刚开始,加油吧少年~