1月15日

1月15日

今天没有思维题,做了最大流题单。

P3376 【模板】网络最大流 - 洛谷 | 计算机科学教育新生态

模板题

#include<bits/stdc++.h>
#define int  long long
#define N 10010
#define M 200010
using namespace std;

int n,m,S,T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];

void add(int a,int b,int c){
    e[++idx]={b,c,h[a]};
    h[a]=idx;
}
bool bfs(){ //对点分层,找增广路
    memset(d,0,sizeof d);
    queue<int>q;
    q.push(S); d[S]=1;
    while(q.size()){
        int u=q.front(); q.pop();
        for(int i=h[u];i;i=e[i].last){
            int v=e[i].v;
            if(d[v]==0 && e[i].c){
                d[v]=d[u]+1;
                q.push(v);
                if(v==T)return true;
            }
        }
    }
    return false;
}
int dfs(int u, int mf){ //多路增广
    if(u==T) return mf;
    int sum=0;
    for(int i=cur[u];i;i=e[i].last){
        cur[u]=i; //当前弧优化
        int v=e[i].v;
        if(d[v]==d[u]+1 && e[i].c){
            int f=dfs(v,min(mf,e[i].c));
            e[i].c-=f;
            e[i^1].c+=f; //更新残留网
            sum+=f; //累加u的流出流量
            mf-=f;  //减少u的剩余流量
            if(mf==0)break;//余量优化
        }
    }
    if(sum==0) d[u]=0; //残枝优化
    return sum;
}
int dinic(){ //累加可行流
    int flow=0;
    while(bfs()){
        memcpy(cur, h, sizeof h);
        flow+=dfs(S,1e9);
    }
    return flow;
}
signed main() {
    int a, b, c;
    cin>>n>>m>>S>>T;
    while (m--) {
        cin>>a>>b>>c;
        add(a, b, c);
        add(b, a, 0);
    }
    cout<<dinic()<<endl;
    return 0;
}

P4722 【模板】最大流 加强版 / 预流推进 - 洛谷 | 计算机科学教育新生态

因为dinic的复杂度上限是O(n^{2}*m),所以会有卡dinic的题,所以就有了预留推进算法——hlpp,实在是太抽象,没看懂也懒得看,所以就记了个板子。以后再看看。

#include <bits/stdc++.h>
using namespace std;
inline int Read(){
    int x=0;
    char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
inline void write(int x){
    if(x < 0){//是负数提前输出负号
        putchar('-');//字符输出
        x = -x;//转成正数处理
    }
    if(x > 9){//不是一位数
        write(x / 10);//递归——直到最高位
    }
    putchar(x % 10 + '0');//转成字符输出
    return;
}
const int inf=1<<30;
int top=1,head[10100];
int n,m,s,t;
int e[10100],h[10100],cnth[20100];//每个点对应的余流,高度;每个高度有多少个点
struct cmp{
    inline bool operator () (int a,int b) const{
        return h[a]<h[b];
    }
};
struct Node{
    int v;
    int val;
    int next;
}node[400100];
inline void addedge(int u,int v,int val){
    node[++top].v=v;
    node[top].val=val;
    node[top].next=head[u];
    head[u]=top;
    return;
}
inline void add(int u,int v,int val){
    addedge(u,v,val);
    addedge(v,u,0);
    return;
}
int inque[11000];
void bfs(){
    memset(h,0x3f,sizeof(h));
    h[t]=0;
    queue<int>qu;
    qu.push(t);
    while(!qu.empty()){
        int u=qu.front();
        qu.pop();
        inque[u]=0;
        for(int i=head[u];i;i=node[i].next){
            int d=node[i].v;
            if(node[i^1].val&&h[d]>h[u]+1){//反向跑
                h[d]=h[u]+1;
                if(inque[d]==0){
                    qu.push(d);
                    inque[d]=1;
                }
            }
        }
    }
    return;
}
priority_queue<int,vector<int>,cmp>q;
inline void push_(int u){
    for(int i=head[u];i;i=node[i].next){
        int d=node[i].v;
        if(node[i].val&&h[d]+1==h[u]){//可以推流
            int mi=min(node[i].val,e[u]);
            node[i].val-=mi;
            node[i^1].val+=mi;
            e[u]-=mi;
            e[d]+=mi;
            if(inque[d]==0&&d!=t&&d!=s){
                q.push(d);
                inque[d]=1;
            }
            if(e[u]==0)break;//已经推完了
        }
    }
    return;
}//推流
inline void relabel(int u){
    h[u]=inf;
    for(int i=head[u];i;i=node[i].next){
        int d=node[i].v;
        if(node[i].val&&h[d]+1<h[u]){
            h[u]=h[d]+1;
        }
    }
    return;
}//把u的高度更改为与u相邻的最低的点的高度加1
int hlpp(){
    int i;
    bfs();
    if(h[s]==0x3f3f3f3f)return 0;//s与t不连通
    h[s]=n;
    for(i=1;i<=n;i++)if(h[i]<0x3f3f3f3f)cnth[h[i]]++;//统计各个高度的点数,注意不要让下标越界
    for(i=head[s];i;i=node[i].next){
        int d=node[i].v;
        int mi=node[i].val;
        if(mi){
            e[s]-=mi;
            e[d]+=mi;
            node[i].val-=mi;
            node[i^1].val+=mi;
            if(d!=t&&inque[d]==0&&d!=s){
                q.push(d);
                inque[d]=1;
            }
        }
    }//从s向周围点推流
    while(!q.empty()){
        int u=q.top();
        inque[u]=0;
        q.pop();
        push_(u);
        if(e[u]){//还有余流
            cnth[h[u]]--;
            if(cnth[h[u]]==0){
                for(int i=1;i<=n;i++){
                    if(i!=s&&i!=t&&h[i]>h[u]&&h[i]<n+1){
                        h[i]=n+1;//标记无法到达
                    }
                }
            }//gap优化
            relabel(u);
            cnth[h[u]]++;
            q.push(u);
            inque[u]=1;
        }
    }
    return e[t];
}
signed main(){
    n=Read(),m=Read(),s=Read(),t=Read();
    int i;
    int u,v,val;
    for(i=1;i<=m;i++)u=Read(),v=Read(),val=Read(),add(u,v,val);
    printf("%d\n",hlpp());
    return 0;
}

[P2740 USACO4.2] 草地排水Drainage Ditches - 洛谷 | 计算机科学教育新生态

就是最大流的板子题

#include<bits/stdc++.h>
#define int  long long
#define N 10010
#define M 200010
using namespace std;

int n,m;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];

void add(int a,int b,int c){
    e[++idx]={b,c,h[a]};
    h[a]=idx;
}
bool bfs(int S){ //对点分层,找增广路
    memset(d,0,sizeof d);
    queue<int>q;
    q.push(S); d[S]=1;
    while(q.size()){
        int u=q.front(); q.pop();
        for(int i=h[u];i;i=e[i].last){
            int v=e[i].v;
            if(d[v]==0 && e[i].c){
                d[v]=d[u]+1;
                q.push(v);
                if(v==T)return true;
            }
        }
    }
    return false;
}
int dfs(int u, int mf){ //多路增广
    if(u==T) return mf;
    int sum=0;
    for(int i=cur[u];i;i=e[i].last){
        cur[u]=i; //当前弧优化
        int v=e[i].v;
        if(d[v]==d[u]+1 && e[i].c){
            int f=dfs(v,min(mf,e[i].c));
            e[i].c-=f;
            e[i^1].c+=f; //更新残留网
            sum+=f; //累加u的流出流量
            mf-=f;  //减少u的剩余流量
            if(mf==0)break;//余量优化
        }
    }
    if(sum==0) d[u]=0; //残枝优化
    return sum;
}
int dinic(int S){ //累加可行流
    int flow=0;
    while(bfs(S)){
        memcpy(cur, h, sizeof h);
        flow+=dfs(S,1e9);
    }
    return flow;
}
signed main() {
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int u,v,w;
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,0);
    }
    T=m;
    cout<<dinic(1)<<endl;
    return 0;
}

[P2065 TJOI2011] 卡片 - 洛谷 | 计算机科学教育新生态

最大流的匹配问题

题意:桌子上有红蓝两种颜色的卡牌,并且每张卡牌有一个数,目的是拿走一些组卡牌,每一组所有卡牌颜色不能一致,且任意两张卡牌不互质。问:最多可以从桌上拿走多少组卡片。

思路:首先可以明白,每组卡牌选两张是最优的,建造网络模型,将红蓝颜色分为两组,并且将不互质的数都与质因子建边,且流量都为1(因为每张牌只选一次)。所以我们先质数筛,然后建边,跑最大流就行了。最大流即为答案。

#include<bits/stdc++.h>
#define int  long long
using namespace std;
const int N=10010,M=200010;
int n,m;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];

void add(int a,int b,int c) {
    e[++idx] = {b, c, h[a]};
    h[a] = idx;
}
bool bfs(int S) { //对点分层,找增广路
    memset(d, 0, sizeof d);
    queue<int> q;
    q.push(S);
    d[S] = 1;
    while (q.size()) {
        int u = q.front();
        q.pop();
        for (int i = h[u]; i; i = e[i].last) {
            int v = e[i].v;
            if (d[v] == 0 && e[i].c) {
                d[v] = d[u] + 1;
                q.push(v);
                if (v == T)return true;
            }
        }
    }
    return false;
}
int dfs(int u, int mf) { //多路增广
    if (u == T) return mf;
    int sum = 0;
    for (int i = cur[u]; i; i = e[i].last) {
        cur[u] = i; //当前弧优化
        int v = e[i].v;
        if (d[v] == d[u] + 1 && e[i].c) {
            int f = dfs(v, min(mf, e[i].c));
            e[i].c -= f;
            e[i ^ 1].c += f; //更新残留网
            sum += f; //累加u的流出流量
            mf -= f;  //减少u的剩余流量
            if (mf == 0)break;//余量优化
        }
    }
    if (sum == 0) d[u] = 0; //残枝优化
    return sum;
}
int dinic(int S) { //累加可行流
    int flow = 0;
    while (bfs(S)) {
        memcpy(cur, h, sizeof h);
        flow += dfs(S, 1e9);
    }
    return flow;
}
int blue[505],red[505];
int vis[N];  //划掉合数
int prim[N]; //记录质数
int cnt; //质数个数

void get_prim(int n) { //欧拉筛法-----O(N)
    vis[1]=1;
    for (int i = 2; i <= n; i++) {//越界中断
        if (!vis[i]) prim[++cnt] = i;
        for (int j = 1; i * prim[j] <= n; j++) {//乘以已经记录的数,越界中断,开筛
            vis[i * prim[j]] = 1;
            if (i % prim[j] == 0) break;//整除中断,保证被最小的质因子prim[j]划掉
        }
    }
}
void init() {
    memset(h, 0, sizeof h);
    idx = 1;
}
void solve() {
    init();
    cin >> m >> n;
    T = 1e7;
    int num = 1;
    for (int i = 1; i <= m; i++) cin >> blue[i];
    for (int i = 1; i <= n; i++) cin >> red[i];
    for (int i = 1; i <= m; i++) {
        add(1, i + 1, 1);
        add(i + 1, 1, 0);
    }
    num += m;
    for (int i = 1; i <= m; i++) {
        int tmp = blue[i];
        int pos = i + 1;
        for (int j = 1; j <= cnt; j++) {
            if (prim[j] > tmp) break;
            if (tmp % prim[j] == 0) {
                add(pos, num + j, 1);
                add(num + j, pos, 0);
            }
        }
    }
    int num1 = num + cnt;
    map<int, int> mp;
    for (int i = 1; i <= n; i++) {
        mp[i] = ++num1;
    }
    for (int i = 1; i <= n; i++) {
        int tmp = red[i];
        for (int j = 1; j <= cnt; j++) {
            if (prim[j] > tmp) break;
            if (tmp % prim[j] == 0) {
                add(num + j, mp[i], 1);
                add(mp[i], num + j, 0);
            }
        }
    }
    T = num1 + 1;
    for (int i = 1; i <= n; i++) {
        add(mp[i], T, 1);
        add(T, mp[i], 0);
    }
    cout << dinic(1) << endl;
}
signed main() {
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _;
    cin >> _;
    int nn = 1e4;
    get_prim(nn);
    while (_--)
        solve();
    return 0;
}

P2763 试题库问题 - 洛谷 | 计算机科学教育新生态

和上题思路差不多,但是问题目的不同,求的是任意一个方案,上题是最大的组数,跑完最大流之后,剩余流量为0的点就是被选的数。建图和上题类似。

#include<bits/stdc++.h>
#define int  long long
using namespace std;
const int N=10010,M=200010;
int T;
struct edge{int v,c,last;}e[M];
int h[N],idx=1; //从2,3开始配对
int d[N],cur[N];

void add(int a,int b,int c) {
    e[++idx] = {b, c, h[a]};
    h[a] = idx;
}
bool bfs(int S) { //对点分层,找增广路
    memset(d, 0, sizeof d);
    queue<int> q;
    q.push(S);
    d[S] = 1;
    while (q.size()) {
        int u = q.front();
        q.pop();
        for (int i = h[u]; i; i = e[i].last) {
            int v = e[i].v;
            if (d[v] == 0 && e[i].c) {
                d[v] = d[u] + 1;
                q.push(v);
                if (v == T)return true;
            }
        }
    }
    return false;
}
int dfs(int u, int mf) { //多路增广
    if (u == T) return mf;
    int sum = 0;
    for (int i = cur[u]; i; i = e[i].last) {
        cur[u] = i; //当前弧优化
        int v = e[i].v;
        if (d[v] == d[u] + 1 && e[i].c) {
            int f = dfs(v, min(mf, e[i].c));
            e[i].c -= f;
            e[i ^ 1].c += f; //更新残留网
            sum += f; //累加u的流出流量
            mf -= f;  //减少u的剩余流量
            if (mf == 0)break;//余量优化
        }
    }
    if (sum == 0) d[u] = 0; //残枝优化
    return sum;
}
int dinic(int S) { //累加可行流
    int flow = 0;
    while (bfs(S)) {
        memcpy(cur, h, sizeof h);
        flow += dfs(S, 1e9);
    }
    return flow;
}

void init() {
    memset(h, 0, sizeof h);
    idx = 1;
}
vector<int> ans[N];
void solve() {
    init();
    int k,n;
    cin>>k>>n;
    for(int i=1;i<=n;i++){
        add(0,i,1);
        add(i,0,0);
    }
    map<int,int> mp;
    int cnt=n;
    for(int i=1;i<=k;i++){
        mp[i]=++cnt;
    }
    T=cnt+1;
    vector<int> num(k+1,0);
    for(int i=1;i<=k;i++){
        int x;
        cin>>x;
        num[i]=x;
    }
    for(int i=1;i<=n;i++){
        int p;
        cin>>p;
        for(int j=1;j<=p;j++){
            int y;
            cin>>y;
            add(i,mp[y],1);
            add(mp[y],i,0);
        }
    }
    for(int i=1;i<=k;i++){
        add(mp[i],T,num[i]);
        add(T,mp[i],0);
    }
    dinic(0);
    for(int i=1;i<=n;i++){
        for(int j=h[i];j;j=e[j].last){
            if(e[j].c==0){
                ans[e[j].v].push_back(i);
            }
        }
    }
    for(int i=1;i<=k;i++){
        if(ans[mp[i]].size()<num[i]){
            cout<<"No Solution!"<<endl;
            return;
        }
    }
    for(int i=1;i<=k;i++){
        cout<<i<<": ";
        int cc=0;
        for(auto j:ans[mp[i]]){
            if(cc>=num[i]) break;
            cc++;
            cout<<j<<" ";
        }
        cout<<endl;
    }
}
signed main() {
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//    int _;
//    cin >> _;
//    while (_--)
        solve();
    return 0;
}
最大流匹配问题总结

总结一下最大流的匹配问题

网络流擅长于解决各种有条件要求的匹配,重在如何建图。

建图要首先构造出网络模型:

  1. 将所有待匹配的点分为几组,建立两个虚拟点,也就是源点和汇点,没有组别就谈不上匹配了。
  2. 将点建边,边流量的确定是依据选点的限制条件来的,比如每组中的点只选一个,则组和组中点的边流量为1,
  3. 依据题目目的求网络流相应的结果。
posted @ 2025-01-16 08:42  _LXYYYY  阅读(11)  评论(0)    收藏  举报