8.11 暑假集训——网络流篇

第一次接触网络流,还不习惯用这种很大块而且不知道内部原理的“模板”,熟悉花了挺长时间,勉强算是会用了吧
对最大流,最省费用最大流的意义还需要加深理解,可以多复习一下最后一题,然后,刷题–

前两道是模板题
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;
}

Drainage Ditches

#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;
}

图论这才刚刚开始,加油吧少年~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值